<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://socialledge.com/sjsu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=243+user3</id>
		<title>Embedded Systems Learning Academy - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="http://socialledge.com/sjsu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=243+user3"/>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php/Special:Contributions/243_user3"/>
		<updated>2026-05-06T07:40:18Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.27.1</generator>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71620</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71620"/>
				<updated>2024-05-23T03:52:48Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Checkpoint Algorithm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&lt;br /&gt;
In the provided function, we use the Haversine algorithm to calculate the shortest distance between two geographical points on Earth, identified by their latitude and longitude. We start by converting these coordinates from degrees to radians and then apply the Haversine formula to determine the distance. Additionally, we calculate the bearing and heading from the current location to the destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gps_coordinates_t find_next_point(gps_coordinates_t origin, gps_coordinates_t destination) {&lt;br /&gt;
  gps_coordinates_t closest_point;&lt;br /&gt;
  float min_distance = FLT_MAX;&lt;br /&gt;
  float distance;&lt;br /&gt;
  int checkpoint_idx = -1;&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; NUM_OF_CHECKPOINTS; i++) {&lt;br /&gt;
    distance = get_distance_between_points(origin, locations_we_can_travel[i]);&lt;br /&gt;
    if (distance &amp;lt; min_distance) {&lt;br /&gt;
      if (get_distance_between_points(origin, locations_we_can_travel[i]) &amp;lt; 5) {&lt;br /&gt;
        printf(&amp;quot;reached checkpoint\n&amp;quot;);&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
      // Check if point is actually closer to destincation&lt;br /&gt;
      if (get_distance_between_points(locations_we_can_travel[i], destination) &amp;lt;&lt;br /&gt;
          get_distance_between_points(origin, destination)) {&lt;br /&gt;
        min_distance = distance;&lt;br /&gt;
        checkpoint_idx = i;&lt;br /&gt;
        printf(&amp;quot;setting checkpoint\n&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (checkpoint_idx &amp;lt; 0) {&lt;br /&gt;
    closest_point = destination;&lt;br /&gt;
    printf(&amp;quot;Going destination\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    printf(&amp;quot;setting closest_point\n&amp;quot;);&lt;br /&gt;
    closest_point = locations_we_can_travel[checkpoint_idx];&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;p: %f, %f&amp;quot;, closest_point.latitude, closest_point.longitude);&lt;br /&gt;
  return closest_point;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71619</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71619"/>
				<updated>2024-05-23T03:47:13Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Checkpoint Algorithm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&lt;br /&gt;
In the provided function, we use the Haversine algorithm to calculate the shortest distance between two geographical points on Earth, identified by their latitude and longitude. We start by converting these coordinates from degrees to radians and then apply the Haversine formula to determine the distance. Additionally, we calculate the bearing and heading from the current location to the destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static const gps_coordinates_t locations_we_can_travel[NUM_OF_CHECKPOINTS] = {&lt;br /&gt;
    //(la, lo)&lt;br /&gt;
    // example values&lt;br /&gt;
    {50, 30},&lt;br /&gt;
    {20, 10},&lt;br /&gt;
    {30, 90},&lt;br /&gt;
    {35, -45},&lt;br /&gt;
    /*{37.339084, -121.880836},&lt;br /&gt;
    {37.339178, -121.880861},&lt;br /&gt;
    {37.339318, -121.880970},&lt;br /&gt;
    {37.339382, -121.881054},*/&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
gps_coordinates_t find_next_point(gps_coordinates_t origin, gps_coordinates_t destination) {&lt;br /&gt;
  gps_coordinates_t closest_point;&lt;br /&gt;
  float min_distance = FLT_MAX;&lt;br /&gt;
  float distance;&lt;br /&gt;
  int checkpoint_idx = -1;&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; NUM_OF_CHECKPOINTS; i++) {&lt;br /&gt;
    distance = get_distance_between_points(origin, locations_we_can_travel[i]);&lt;br /&gt;
    if (distance &amp;lt; min_distance) {&lt;br /&gt;
      if (get_distance_between_points(origin, locations_we_can_travel[i]) &amp;lt; 5) {&lt;br /&gt;
        printf(&amp;quot;reached checkpoint\n&amp;quot;);&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
      // Check if point is actually closer to destincation&lt;br /&gt;
      if (get_distance_between_points(locations_we_can_travel[i], destination) &amp;lt;&lt;br /&gt;
          get_distance_between_points(origin, destination)) {&lt;br /&gt;
        min_distance = distance;&lt;br /&gt;
        checkpoint_idx = i;&lt;br /&gt;
        printf(&amp;quot;setting checkpoint\n&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (checkpoint_idx &amp;lt; 0) {&lt;br /&gt;
    closest_point = destination;&lt;br /&gt;
    printf(&amp;quot;Going destination\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    printf(&amp;quot;setting closest_point\n&amp;quot;);&lt;br /&gt;
    closest_point = locations_we_can_travel[checkpoint_idx];&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;p: %f, %f&amp;quot;, closest_point.latitude, closest_point.longitude);&lt;br /&gt;
  return closest_point;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71617</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71617"/>
				<updated>2024-05-23T03:45:16Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Haversine Algorithm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&lt;br /&gt;
In the provided function, we use the Haversine algorithm to calculate the shortest distance between two geographical points on Earth, identified by their latitude and longitude. We start by converting these coordinates from degrees to radians and then apply the Haversine formula to determine the distance. Additionally, we calculate the bearing and heading from the current location to the destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
gps_coordinates_t find_next_point(gps_coordinates_t origin, gps_coordinates_t destination) {&lt;br /&gt;
  gps_coordinates_t closest_point;&lt;br /&gt;
  float min_distance = FLT_MAX;&lt;br /&gt;
  float distance;&lt;br /&gt;
  int checkpoint_idx = -1;&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; NUM_OF_CHECKPOINTS; i++) {&lt;br /&gt;
    distance = get_distance_between_points(origin, locations_we_can_travel[i]);&lt;br /&gt;
    if (distance &amp;lt; min_distance) {&lt;br /&gt;
      if (get_distance_between_points(origin, locations_we_can_travel[i]) &amp;lt; 5) {&lt;br /&gt;
        printf(&amp;quot;reached checkpoint\n&amp;quot;);&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
      // Check if point is actually closer to destincation&lt;br /&gt;
      if (get_distance_between_points(locations_we_can_travel[i], destination) &amp;lt;&lt;br /&gt;
          get_distance_between_points(origin, destination)) {&lt;br /&gt;
        min_distance = distance;&lt;br /&gt;
        checkpoint_idx = i;&lt;br /&gt;
        printf(&amp;quot;setting checkpoint\n&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (checkpoint_idx &amp;lt; 0) {&lt;br /&gt;
    closest_point = destination;&lt;br /&gt;
    printf(&amp;quot;Going destination\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    printf(&amp;quot;setting closest_point\n&amp;quot;);&lt;br /&gt;
    closest_point = locations_we_can_travel[checkpoint_idx];&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;p: %f, %f&amp;quot;, closest_point.latitude, closest_point.longitude);&lt;br /&gt;
  return closest_point;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71616</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71616"/>
				<updated>2024-05-23T03:43:30Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Checkpoint Algorithm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
gps_coordinates_t find_next_point(gps_coordinates_t origin, gps_coordinates_t destination) {&lt;br /&gt;
  gps_coordinates_t closest_point;&lt;br /&gt;
  float min_distance = FLT_MAX;&lt;br /&gt;
  float distance;&lt;br /&gt;
  int checkpoint_idx = -1;&lt;br /&gt;
&lt;br /&gt;
  for (int i = 0; i &amp;lt; NUM_OF_CHECKPOINTS; i++) {&lt;br /&gt;
    distance = get_distance_between_points(origin, locations_we_can_travel[i]);&lt;br /&gt;
    if (distance &amp;lt; min_distance) {&lt;br /&gt;
      if (get_distance_between_points(origin, locations_we_can_travel[i]) &amp;lt; 5) {&lt;br /&gt;
        printf(&amp;quot;reached checkpoint\n&amp;quot;);&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
      // Check if point is actually closer to destincation&lt;br /&gt;
      if (get_distance_between_points(locations_we_can_travel[i], destination) &amp;lt;&lt;br /&gt;
          get_distance_between_points(origin, destination)) {&lt;br /&gt;
        min_distance = distance;&lt;br /&gt;
        checkpoint_idx = i;&lt;br /&gt;
        printf(&amp;quot;setting checkpoint\n&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        continue;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (checkpoint_idx &amp;lt; 0) {&lt;br /&gt;
    closest_point = destination;&lt;br /&gt;
    printf(&amp;quot;Going destination\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
    printf(&amp;quot;setting closest_point\n&amp;quot;);&lt;br /&gt;
    closest_point = locations_we_can_travel[checkpoint_idx];&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;p: %f, %f&amp;quot;, closest_point.latitude, closest_point.longitude);&lt;br /&gt;
  return closest_point;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71615</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71615"/>
				<updated>2024-05-23T03:42:16Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Checkpoint Algorithm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
abdc&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71613</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71613"/>
				<updated>2024-05-23T03:40:01Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Geographical Controller */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Checkpoint Algorithm ====&lt;br /&gt;
The checkpoint algorithm is a very simple algo that provides the next &amp;quot;checkpoint&amp;quot; to drive to given the current location and destination coordinates. We first try to find the closest checkpoint, and if it brings us closer to the final destination, then travel to that checkpoint. We repeat this process until no checkpoint can bring us closer and the destination is the final &amp;quot;checkpoint.&amp;quot; We use the haversine algorithm to determine how close we are to a checkpoint so that we know we reached it, otherwise we may circle back around to it. &lt;br /&gt;
&lt;br /&gt;
If no checkpoints are provided in the list, then the car will attempt to navigate to the final destination directly which will likely be very challenging for large obstacles in the path. Initially, we used Euclidian distance to find the closest checkpoint, but raw distance calculated from that formula is not very easy to understand, so we switch to using haversine to get distance in meters. &lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71612</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71612"/>
				<updated>2024-05-23T03:25:36Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Project Video */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
[https://drive.google.com/file/d/1A88K5aRfS_7tnjLPoShG8fn4YsLxWrRU/view?usp=drive_link Demo]&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71611</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71611"/>
				<updated>2024-05-23T03:16:41Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Team Members &amp;amp; Responsibilities */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
[[File: TeamX_GroupPicture.jpeg]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:TeamX_GroupPicture.jpeg&amp;diff=71610</id>
		<title>File:TeamX GroupPicture.jpeg</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:TeamX_GroupPicture.jpeg&amp;diff=71610"/>
				<updated>2024-05-23T03:15:29Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71609</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71609"/>
				<updated>2024-05-23T03:00:09Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
The emergency stop task is a task that is constantly sending a &amp;quot;stop&amp;quot; flag. This flag is false by default and only set true if received from the APP. Once the vehicle stops, our the sensor node must be manually reset to continue operation. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71608</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71608"/>
				<updated>2024-05-23T02:57:53Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
The bridge task is responsible for receiving data from the APP via bluetooth. We utilize a Adafruit module (see above for part) which acts as a uart-bluetooth bridge making it extremely easy to integrate into our system without diving too deep into making bluetooth drivers. The main messages we receive are the GPS destination coordinates, and an emergency stop request. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71607</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71607"/>
				<updated>2024-05-23T02:56:02Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
&lt;br /&gt;
The ultrasonic get sensor data task occurs in the 1hz, but we range 8 times in the 1hz function. The 3 ultrasonic sensors are chained together to prevent crosstalk. The SJ2 board will send a trigger signal to the left sensor which will then trigger the middle sensor, then the left. This process repeats 8 times in 1 second. Due to the chaining, the sample rate is significantly reduced, but the incoming data for each sensor is more accurate. Without the chaining, the sensor readings appear to be unstable. &lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71606</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71606"/>
				<updated>2024-05-22T22:56:03Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Schedule */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71594</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71594"/>
				<updated>2024-05-22T18:55:15Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Technical Challenges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge we encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, we used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, we hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, we implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71593</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71593"/>
				<updated>2024-05-22T18:35:57Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Technical Challenges */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The first challenge I encountered involved identifying the read/write characteristics of the scanned peripherals. The UUIDs for the read and write characteristics weren't being detected when sifting through nearby Bluetooth devices. To resolve this, I used a different Bluetooth testing app that successfully parsed and identified all relevant UUIDs. Subsequently, I hardcoded these UUIDs into our Bluetooth module to enable data transmission and reception. &lt;br /&gt;
&lt;br /&gt;
The second issue arose with the transmission of latitude and longitude coordinates selected on the map view. Since the map view operated as a separate screen within the app, the latitude and longitude values from the dropped pin were not accessible from the main view controller screen. To address this, I implemented a singleton to store these coordinates globally, allowing them to be accessed and updated across different screens effectively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71592</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71592"/>
				<updated>2024-05-22T18:22:52Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Receive GEO Status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
    if let error = error {&lt;br /&gt;
        print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
    }&lt;br /&gt;
    guard let characteristics = service.characteristics else {&lt;br /&gt;
        print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for characteristic in characteristics {&lt;br /&gt;
        print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Check if this characteristic supports write operations&lt;br /&gt;
        if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
            writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            readCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Now that we have the read characteristic, we can read data&lt;br /&gt;
            peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
            peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
            print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71591</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71591"/>
				<updated>2024-05-22T18:22:06Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Receive GEO Status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error discovering characteristics: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        guard let characteristics = service.characteristics else {&lt;br /&gt;
            print(&amp;quot;No characteristics discovered for service: \(service.uuid)&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        for characteristic in characteristics {&lt;br /&gt;
            print(&amp;quot;Discovered characteristic: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
            &lt;br /&gt;
            // Check if this characteristic supports write operations&lt;br /&gt;
            if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {&lt;br /&gt;
                writeCharacteristic = characteristic&lt;br /&gt;
                print(&amp;quot;Write characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
                &lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            if characteristic.uuid == CBUUID(string: &amp;quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
                readCharacteristic = characteristic&lt;br /&gt;
                print(&amp;quot;Read characteristic found: \(characteristic.uuid)&amp;quot;)&lt;br /&gt;
                &lt;br /&gt;
                // Now that we have the read characteristic, we can read data&lt;br /&gt;
                peripheral.setNotifyValue(true, for: characteristic) // Enable notifications for this characteristic&lt;br /&gt;
                peripheral.readValue(for: characteristic) // Read the initial value&lt;br /&gt;
                print(&amp;quot;Reading data...&amp;quot;)&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71590</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71590"/>
				<updated>2024-05-22T18:17:34Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Send Emergency Stop */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
@IBAction func buttonPressed2(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;STOP Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        sendStopToPeripheral()&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController {&lt;br /&gt;
    func sendStopToPeripheral() {&lt;br /&gt;
        guard let peripheral = selectedPeripheral,&lt;br /&gt;
              let characteristic = writeCharacteristic,&lt;br /&gt;
              characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse),&lt;br /&gt;
              let stopData = &amp;quot;S&amp;quot;.data(using: .utf8),&lt;br /&gt;
              let stringRepresentation = String(data: stopData, encoding: .utf8) else {&lt;br /&gt;
                print(&amp;quot;Cannot send data: Peripheral or characteristic not set up correctly.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
          &lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
        print(&amp;quot;stopData sent: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if characteristic.uuid == CBUUID(string: &amp;quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&amp;quot;) {&lt;br /&gt;
            //writeCharacteristic = characteristic&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic FOUND&amp;quot;)&lt;br /&gt;
            peripheral.writeValue(stopData, for: characteristic, type: .withResponse) // or .withoutResponse&lt;br /&gt;
            print(&amp;quot;stopData sent: \(stopData)&amp;quot;)&lt;br /&gt;
            print(&amp;quot;stopData sent str: \(stringRepresentation)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        else{&lt;br /&gt;
            print(&amp;quot;Adafruit BLE write characteristic NOT FOUND&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71589</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71589"/>
				<updated>2024-05-22T18:16:50Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Set GPS Desination */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
import UIKit&lt;br /&gt;
import MapKit&lt;br /&gt;
&lt;br /&gt;
protocol MapViewControllerDelegate: AnyObject {&lt;br /&gt;
    func didChooseLocation(latitude: Double, longitude: Double)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class CustomMapViewController: UIViewController, MKMapViewDelegate {&lt;br /&gt;
&lt;br /&gt;
    @IBOutlet weak var mapView: MKMapView!&lt;br /&gt;
    weak var delegate: MapViewControllerDelegate?&lt;br /&gt;
    &lt;br /&gt;
    var lastSelectedLatitude: Double?&lt;br /&gt;
    var lastSelectedLongitude: Double?&lt;br /&gt;
    &lt;br /&gt;
    override func viewDidLoad() {&lt;br /&gt;
        super.viewDidLoad()&lt;br /&gt;
        mapView.delegate = self&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;Map View loaded!&amp;quot;)&lt;br /&gt;
        // Add a tap gesture recognizer to the map view&lt;br /&gt;
        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))&lt;br /&gt;
        mapView.addGestureRecognizer(tapRecognizer)&lt;br /&gt;
        &lt;br /&gt;
        // Define the center of the region your map should display&lt;br /&gt;
        // 37.33919199814887, -121.88103575173972&lt;br /&gt;
        let center = CLLocationCoordinate2D(latitude: 37.33919199814887, longitude: -121.88103575173972) // SJSU parking lot&lt;br /&gt;
        &lt;br /&gt;
        // Set the region of the map&lt;br /&gt;
        let region = MKCoordinateRegion(center: center, span: span)&lt;br /&gt;
        mapView.setRegion(region, animated: true)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @objc func handleMapTap(_ gestureReconizer: UITapGestureRecognizer) {&lt;br /&gt;
        let location = gestureReconizer.location(in: mapView)&lt;br /&gt;
        let coordinate = mapView.convert(location, toCoordinateFrom: mapView)&lt;br /&gt;
&lt;br /&gt;
        // Remove all existing annotations&lt;br /&gt;
        mapView.removeAnnotations(mapView.annotations)&lt;br /&gt;
        &lt;br /&gt;
        // Add annotation:&lt;br /&gt;
        let annotation = MKPointAnnotation()&lt;br /&gt;
        annotation.coordinate = coordinate&lt;br /&gt;
        mapView.addAnnotation(annotation)&lt;br /&gt;
        &lt;br /&gt;
        lastSelectedLatitude = coordinate.latitude&lt;br /&gt;
        lastSelectedLongitude = coordinate.longitude&lt;br /&gt;
        &lt;br /&gt;
        print(&amp;quot;Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Sending GPS destination location to Bridge Controller!&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        if let currentlatitude = lastSelectedLatitude, let currentlongitude = lastSelectedLongitude {&lt;br /&gt;
            delegate?.didChooseLocation(latitude: currentlatitude, longitude: currentlongitude)&lt;br /&gt;
            print(&amp;quot;Coordinates sent: Latitude: \(currentlatitude), Longitude: \(currentlongitude)&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;No location selected.&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71588</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71588"/>
				<updated>2024-05-22T18:15:28Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Scan Bluetooth Devices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
 @IBAction func buttonPressed(_ sender: UIButton) {&lt;br /&gt;
        print(&amp;quot;Scan bluetooth devices Button pressed&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Start scanning for Bluetooth devices if Bluetooth is turned on&lt;br /&gt;
        if centralManager.state == .poweredOn {&lt;br /&gt;
            centralManager.scanForPeripherals(withServices: nil, options: nil)&lt;br /&gt;
            print(&amp;quot;Scanning for Bluetooth devices...&amp;quot;)&lt;br /&gt;
        } else {&lt;br /&gt;
            print(&amp;quot;Bluetooth is not turned on&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71587</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71587"/>
				<updated>2024-05-22T18:14:16Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Scan Bluetooth Devices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
extension ViewController: CBCentralManagerDelegate {&lt;br /&gt;
    func centralManagerDidUpdateState(_ central: CBCentralManager) {&lt;br /&gt;
        switch central.state {&lt;br /&gt;
        case .poweredOn:&lt;br /&gt;
            print(&amp;quot;Bluetooth is On&amp;quot;)&lt;br /&gt;
        case .poweredOff:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Off&amp;quot;)&lt;br /&gt;
        case .resetting:&lt;br /&gt;
            print(&amp;quot;Bluetooth is Resetting&amp;quot;)&lt;br /&gt;
        case .unauthorized:&lt;br /&gt;
            print(&amp;quot;Bluetooth not authorized&amp;quot;)&lt;br /&gt;
        case .unsupported:&lt;br /&gt;
            print(&amp;quot;Bluetooth not supported&amp;quot;)&lt;br /&gt;
        case .unknown:&lt;br /&gt;
            print(&amp;quot;Bluetooth unknown state&amp;quot;)&lt;br /&gt;
        @unknown default:&lt;br /&gt;
            print(&amp;quot;A new case was added that is not handled&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {&lt;br /&gt;
        &lt;br /&gt;
        if let name = peripheral.name, name != &amp;quot;Unknown Device&amp;quot; {&lt;br /&gt;
            // Avoid adding duplicates&lt;br /&gt;
            if !discoveredDevices.contains(where: { $0.identifier == peripheral.identifier }) {&lt;br /&gt;
                discoveredDevices.append(peripheral)&lt;br /&gt;
                DispatchQueue.main.async { [weak self] in self?.displayContent.reloadData()&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {&lt;br /&gt;
        print(&amp;quot;Connected to peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        selectedPeripheral = peripheral // Make sure to store the reference to the connected didFailToConnectperipheral&lt;br /&gt;
        peripheral.delegate = self // Set the delegate to self for handling peripheral events&lt;br /&gt;
        peripheral.discoverServices(nil)&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Disconnected from peripheral: \(peripheral)&amp;quot;)&lt;br /&gt;
        if let error = error {&lt;br /&gt;
            print(&amp;quot;Error: \(error.localizedDescription)&amp;quot;)&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {&lt;br /&gt;
        print(&amp;quot;Failed to connect to peripheral: \(peripheral), error: \(error?.localizedDescription ?? &amp;quot;Unknown error&amp;quot;)&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        // Present an alert to the user with the option to try connecting again&lt;br /&gt;
        let alert = UIAlertController(title: &amp;quot;Connection Failed&amp;quot;, message: &amp;quot;Failed to connect to the selected device. Would you like to try again?&amp;quot;, preferredStyle: .alert)&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Try Again&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Try Again&amp;quot;, style: .default) { _ in&lt;br /&gt;
            // Retry connecting to the selected peripheral&lt;br /&gt;
            central.connect(peripheral, options: nil)&lt;br /&gt;
        })&lt;br /&gt;
        &lt;br /&gt;
        // Add a &amp;quot;Cancel&amp;quot; action to the alert&lt;br /&gt;
        alert.addAction(UIAlertAction(title: &amp;quot;Cancel&amp;quot;, style: .cancel, handler: nil))&lt;br /&gt;
        &lt;br /&gt;
        // Present the alert&lt;br /&gt;
        self.present(alert, animated: true, completion: nil)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71586</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71586"/>
				<updated>2024-05-22T18:12:10Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
The iOS Mobile App was designed using Swift. The primary purpose of the app was to connect to the Bridge Controller via bluetooth and send GPS destination and emergency stop message and to receive and display geo controller status heading and bearing data. &lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71585</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71585"/>
				<updated>2024-05-22T18:09:07Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Receive GEO Status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Receive GEO Status ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71584</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71584"/>
				<updated>2024-05-22T18:08:59Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Send Emergency Stop */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Send Emergency Stop ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Receive GEO Status ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71583</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71583"/>
				<updated>2024-05-22T18:08:51Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Set GPS Desination */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set GPS Desination ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Send Emergency Stop ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Receive GEO Status ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71582</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71582"/>
				<updated>2024-05-22T18:08:43Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Scan Bluetooth Devices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Scan Bluetooth Devices ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Set GPS Desination ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Send Emergency Stop ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Receive GEO Status ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71581</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71581"/>
				<updated>2024-05-22T18:08:09Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
== Scan Bluetooth Devices ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Set GPS Desination ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Send Emergency Stop ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Receive GEO Status ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;swift&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71578</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71578"/>
				<updated>2024-05-22T18:03:39Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Mobile Application */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
[[File:App_flow.png]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:App_flow.png&amp;diff=71577</id>
		<title>File:App flow.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:App_flow.png&amp;diff=71577"/>
				<updated>2024-05-22T18:03:08Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71571</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71571"/>
				<updated>2024-05-22T17:29:13Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Compass Module */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71570</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71570"/>
				<updated>2024-05-22T17:28:40Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
[[File:geo_periodics.png]]&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:Geo_periodics.png&amp;diff=71569</id>
		<title>File:Geo periodics.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:Geo_periodics.png&amp;diff=71569"/>
				<updated>2024-05-22T17:27:34Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:Geo_HW.png&amp;diff=71568</id>
		<title>File:Geo HW.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:Geo_HW.png&amp;diff=71568"/>
				<updated>2024-05-22T17:02:03Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71567</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71567"/>
				<updated>2024-05-22T17:01:31Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Hardware Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
[[File: Geo_HW.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71566</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71566"/>
				<updated>2024-05-22T07:55:40Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Software Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
[[File:Sw_design_teamx.png]]&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:Sw_design_teamx.png&amp;diff=71565</id>
		<title>File:Sw design teamx.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:Sw_design_teamx.png&amp;diff=71565"/>
				<updated>2024-05-22T07:54:56Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: 243 user3 uploaded a new version of File:Sw design teamx.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:Sw_design_teamx.png&amp;diff=71564</id>
		<title>File:Sw design teamx.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:Sw_design_teamx.png&amp;diff=71564"/>
				<updated>2024-05-22T07:48:56Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71563</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71563"/>
				<updated>2024-05-22T07:36:40Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Hardware Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71562</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71562"/>
				<updated>2024-05-22T07:35:46Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Hardware Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:sensor_design.png]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=File:Sensor_design.png&amp;diff=71561</id>
		<title>File:Sensor design.png</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=File:Sensor_design.png&amp;diff=71561"/>
				<updated>2024-05-22T07:34:37Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71559</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71559"/>
				<updated>2024-05-22T07:04:45Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Parts List &amp;amp; Cost */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| BLE Module&lt;br /&gt;
| [https://www.adafruit.com/product/2479]&lt;br /&gt;
| 1&lt;br /&gt;
| $17.50&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71439</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71439"/>
				<updated>2024-05-18T19:48:30Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Advice for Future Students */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
* Take advantage of the past reports! Because so many people have done this project, there are a lot of mistakes that have occurred that you can avoid by reading the past reports&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71438</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71438"/>
				<updated>2024-05-18T19:47:48Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
7. [http://socialledge.com/sjsu/index.php/Industrial_Application_using_CAN_Bus#Class_Project_Reports Previous Project Reports]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71437</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71437"/>
				<updated>2024-05-18T19:45:25Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Advice for Future Students */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
* Avoid the LSM compass where possible: The heading angles are not stable, which caused issues for us when handling the &amp;quot;Going to Destination&amp;quot; logic&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71436</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71436"/>
				<updated>2024-05-18T19:44:18Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
&lt;br /&gt;
1. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71435</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71435"/>
				<updated>2024-05-18T19:43:47Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
1. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71434</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71434"/>
				<updated>2024-05-18T19:43:27Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
1. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
5. [https://stackoverflow.com/questions/48663880/gps-nmea-output-getting-valid-gpgsv-but-not-valid-gpgga-gprmc GPS Position Fixing]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
6. [chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://www.mouser.com/datasheet/2/783/BST-BMM150-DS001-01-786480.pdf BMM150 Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71433</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71433"/>
				<updated>2024-05-18T19:41:43Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
1. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
4. [https://stackoverflow.com/questions/36254363/how-to-convert-latitude-and-longitude-of-nmea-format-data-to-decimal Convert NMEA to Latitude Longitude]&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71432</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71432"/>
				<updated>2024-05-18T19:40:47Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Parts List &amp;amp; Cost */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 1&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
1. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	<entry>
		<id>http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71431</id>
		<title>S24: Team X</title>
		<link rel="alternate" type="text/html" href="http://socialledge.com/sjsu/index.php?title=S24:_Team_X&amp;diff=71431"/>
				<updated>2024-05-18T19:40:31Z</updated>
		
		<summary type="html">&lt;p&gt;243 user3: /* Parts List &amp;amp; Cost */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Project Title ==&lt;br /&gt;
Team X&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abstract ==&lt;br /&gt;
Our main goal of this project was to utilize the knowledge acquired from the lectures in CMPE 243. The report summarizes the overall process taken to develop an autonomous driving RC car using FreeRTOS, various periodic tasks, and the CAN bus to combine and run the different code modules.&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
&lt;br /&gt;
The project was divided into 5 modules:&lt;br /&gt;
&lt;br /&gt;
* Sensor Node&lt;br /&gt;
* Motor Node&lt;br /&gt;
* Driver Node&lt;br /&gt;
* Geo Node&lt;br /&gt;
* Mobile Application&lt;br /&gt;
&lt;br /&gt;
=== Team Members &amp;amp; Responsibilities ===&lt;br /&gt;
&amp;lt;Team Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c Gitlab Project - Team X]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Team Members&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task Responsibility&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;| &lt;br /&gt;
* Priyanka Shah&lt;br /&gt;
|&lt;br /&gt;
* Geo Controller, Mobile App&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Faaris Khilji&lt;br /&gt;
| &lt;br /&gt;
* Sensor and Bridge Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* Ashley Ho&lt;br /&gt;
|&lt;br /&gt;
* Driver and LCD Controller&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; style=&amp;quot;text-align: left;&amp;quot;|&lt;br /&gt;
* All/TBD&lt;br /&gt;
|&lt;br /&gt;
* Motor Controller&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Week#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Start Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| End Date&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Task&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 02/19/2024&lt;br /&gt;
| 02/25/2024&lt;br /&gt;
|&lt;br /&gt;
* Read previous projects, gather information, and discuss among the group members.&lt;br /&gt;
* Schedule weekly online meetings&lt;br /&gt;
* Order CAN transceivers&lt;br /&gt;
* Test GPS and UART communication.&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 02/26/2024&lt;br /&gt;
| 03/03/2024&lt;br /&gt;
|&lt;br /&gt;
* Distribute modules to each team member.&lt;br /&gt;
* Create parts list and Bill of Materials&lt;br /&gt;
* Test CAN Bus communication between 2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 03/04/2024&lt;br /&gt;
| 03/11/2024&lt;br /&gt;
|&lt;br /&gt;
* Order all sensors&lt;br /&gt;
* Research on RC car brands and features.&lt;br /&gt;
* Create project schedule and milestones.&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 03/18/2024&lt;br /&gt;
| 03/24/2024&lt;br /&gt;
|&lt;br /&gt;
* Setup project repository&lt;br /&gt;
* Order RC car&lt;br /&gt;
* Test CAN bus with DBC file messages via BusMaster&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 03/25/2024&lt;br /&gt;
| 03/31/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up driver node to handle CAN messages received from sensor node&lt;br /&gt;
* Set up sensor node to send CAN messages&lt;br /&gt;
* Set up motor controller node to receive driver commands&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| 04/01/2024&lt;br /&gt;
| 04/07/2024&lt;br /&gt;
|&lt;br /&gt;
* Set up Geo Controller to receive destination coordinates&lt;br /&gt;
* Link Geo controller to GPS module&lt;br /&gt;
* Integrate sensor hardware into sensor node&lt;br /&gt;
* Test LCD module to display using I2C&lt;br /&gt;
* Begin wiki documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| 04/08/2024&lt;br /&gt;
| 04/14/2024&lt;br /&gt;
|&lt;br /&gt;
* Enhance obstacle avoidance algorithm&lt;br /&gt;
* Integrate RPM and ultrasonic sensor&lt;br /&gt;
* Integrate LCD module with debug messages&lt;br /&gt;
* Setup preliminary mobile app structure&lt;br /&gt;
* Test obstacle avoidance&lt;br /&gt;
* Start working on power supply for SJ2 boards&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 8&lt;br /&gt;
| 04/15/2024&lt;br /&gt;
| 04/21/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Hack RC car's RPM and install RPM sensor&lt;br /&gt;
* Integrate compass module&lt;br /&gt;
* Begin RC car design and assembly&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 9&lt;br /&gt;
| 04/22/2024&lt;br /&gt;
| 04/28/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish RC car prototype 1&lt;br /&gt;
* Start outdoor testing at garage rooftop&lt;br /&gt;
* Complete basic mobile app with destination, start/stop button&lt;br /&gt;
* Interface motor and steering&lt;br /&gt;
* Set up LED fault indicators&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 10&lt;br /&gt;
| 04/29/2024&lt;br /&gt;
| 05/05/2024&lt;br /&gt;
|&lt;br /&gt;
* Edit Wiki report, continue updating through next few weeks&lt;br /&gt;
* Fine tune mobile app to display map to choose destination&lt;br /&gt;
* Display debug information on LCD&lt;br /&gt;
* Continue RC outdoor tests&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 11&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Fine tune obstacle avoidance algorithm&lt;br /&gt;
* Fine tune motor controller and sensor controller&lt;br /&gt;
* Fine tune DBC file messages&lt;br /&gt;
* Continue updating wiki report and documentation&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 12&lt;br /&gt;
| 05/06/2024&lt;br /&gt;
| 05/12/2024&lt;br /&gt;
|&lt;br /&gt;
* Tidy RC car mounted hardware&lt;br /&gt;
* Continue outdoor testing&lt;br /&gt;
* Fine tune navigational system&lt;br /&gt;
* Continue refining obstacle avoidance algorithm&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
* &amp;lt;font color = &amp;quot;green&amp;quot;&amp;gt;Completed&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 13&lt;br /&gt;
| 05/13/2024&lt;br /&gt;
| 05/19/2024&lt;br /&gt;
|&lt;br /&gt;
* Finish writing project report&lt;br /&gt;
* Final RC car tests on North Garage&lt;br /&gt;
* Reimburse team members for RC car parts and sensors&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
* &amp;lt;font color = &amp;quot;yellow&amp;quot;&amp;gt;In Progress&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 14&lt;br /&gt;
| 05/20/2024&lt;br /&gt;
| 05/26/2024&lt;br /&gt;
|&lt;br /&gt;
* Final Project Demos&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* &amp;lt;font color = &amp;quot;red&amp;quot;&amp;gt;Incomplete&lt;br /&gt;
|-&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Parts List &amp;amp; Cost ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Item#&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Part Desciption&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Vendor&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Qty&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Cost&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| RC Car &lt;br /&gt;
| [https://l.messenger.com/l.php?u=https%3A%2F%2Fwww.hobbytown.com%2Ftraxxas-slash-bl2s-1-10-rtr-2wd-brushless-short-course-truck-green-tra58134-4-grn%2Fp1531653&amp;amp;h=AT2UCO46yJC6FLs5eMMt_WG6b_-2srfPT20Ht30KuNimfT0CtY9eEXN27DIXlvtf-1y2p0tokwHIKXE1SR_LavnhOkAFO_w7IiHUljwLAk6A498c2O3RGqQaW_X21kB9LZfa7w Traxxas]&lt;br /&gt;
| 1&lt;br /&gt;
| $250.00&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| RC Car Battery&lt;br /&gt;
| [https://www.hobbytown.com/traxxas-ezpeak-2s-single-completer-pack-multichemistry-battery-charger-tra2992/p581147 Traxxus]&lt;br /&gt;
| 2&lt;br /&gt;
| $110&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| CAN Transceivers MCP2551-I/P&lt;br /&gt;
| [http://www.microchip.com/wwwproducts/en/en010405 Microchip]&lt;br /&gt;
| 8&lt;br /&gt;
| Free Samples&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| MB1010 LV-MaxSonar&lt;br /&gt;
| [https://maxbotix.com/products/mb1010?variant=41051364556851&amp;amp;currency=USD&amp;amp;utm_term=&amp;amp;utm_source=google&amp;amp;utm_medium=cpc&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwo6GyBhBwEiwAzQTmc0ryxB-TKemhuO9FuxpdqVA01VtM8ZU8mlnqouS3UIvo1jzjhxK4SRoCSZAQAvD_BwE MaxBotix]&lt;br /&gt;
| 4&lt;br /&gt;
| $29.95&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| GPS Module&lt;br /&gt;
| [https://www.amazon.com/Navigation-Positioning-Microcontroller-Compatible-Sensitivity/dp/B0B31NRSD2/ref=sr_1_3?crid=O8LGB2AZCR8K&amp;amp;dib=eyJ2IjoiMSJ9.QFx4oBZl1-dUCGV1D5L0XybyGebhmhfPAXI5ECw0VEaanwtTNAbZ2jLtyeXw1-1rqp40g2PVfxx2LxUOZRPhlh3mO8ZWTN9SnkzwfWuBPVSM4e4E33_H6bUyEb4jMuTZLK2C0yiLu6A0zc024ajAqH7LVdrmTMgSc4YnJaSRKbrDYeeLV54PEoneAAd6BigfYQrbG9EuIouTUmV5kAtRnhIyyaMP2gxipOCB_yc4UzA.ZYspItku9lWRgwbcPp9KWQUcP16jgLm9n76jIO-6DDA&amp;amp;dib_tag=se&amp;amp;keywords=gps+module+arduino&amp;amp;qid=1716055609&amp;amp;sprefix=gps+module%2Caps%2C123&amp;amp;sr=8-3 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $18.99&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 6&lt;br /&gt;
| BMM150 Magnetometer&lt;br /&gt;
| [https://www.amazon.com/waveshare-Magnetometer-Measurement-Interface-Raspberry/dp/B0C5XY3J3B/ref=sr_1_1?crid=1E3MMU8B85MFT&amp;amp;dib=eyJ2IjoiMSJ9.WKN3LVNKtVPIBPBc5_emzouPizZxgR3vsz-6R4ST2A5wR3EeyBvmzlTiUkQqXOrjqBTYtVFE6HJwK8v0tyUIm-IxYzxZeCZsfMtPKOqxzeihzH4uCQ5YyukR_72pW255ZSYR9R941H_vSZOeh7dqnVKY8Bx4kdxNcTvyYoOAYLsR5u17q0dgOJzIW0qTQPTkSc-6mvfwrOj7l_XLafIktSrxUjMB4fgLqSEwj_3jZv8.Ic_theuXAJJDADFV1Eu89Hy_Wvl_E3-sROhwDDwLc-Y&amp;amp;dib_tag=se&amp;amp;keywords=magnetometer+bmm150&amp;amp;qid=1716055546&amp;amp;sprefix=magnetometer+bmm150%2Caps%2C131&amp;amp;sr=8-1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $12.49&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 7&lt;br /&gt;
| LCD&lt;br /&gt;
| [https://www.amazon.com/SunFounder-Serial-Module-Display-Arduino/dp/B01GPUMP9C/ref=asc_df_B019K5X53O/?tag=hyprod-20&amp;amp;linkCode=df0&amp;amp;hvadid=693601922380&amp;amp;hvpos=&amp;amp;hvnetw=g&amp;amp;hvrand=15162836172036316902&amp;amp;hvpone=&amp;amp;hvptwo=&amp;amp;hvqmt=&amp;amp;hvdev=c&amp;amp;hvdvcmdl=&amp;amp;hvlocint=&amp;amp;hvlocphy=9032166&amp;amp;hvtargid=pla-563014027379&amp;amp;mcid=d8869fd4bb8c3e78a3b0ed4cc311f13b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjw0YGyBhByEiwAQmBEWtir6ToCY71keb4T8u84nxll4q-pBoWD5p5W88PQJlnLNyOFNyd48BoCXrQQAvD_BwE&amp;amp;th=1 Amazon]&lt;br /&gt;
| 1&lt;br /&gt;
| $11.69&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CAN Communication ==&lt;br /&gt;
Each node is connected to a CAN transceiver that are connected to a bus with a 120 Ohm resistor on each end. We handled each message on the 10Hz, with the sensor and motor messages having the highest priority.&lt;br /&gt;
&lt;br /&gt;
Specifically, we classified each node with these (see table below) message IDs.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Node&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Message ID Range&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| MOTOR&lt;br /&gt;
| 300 - 310&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| SENSOR&lt;br /&gt;
| 200&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| GEO&lt;br /&gt;
| 400 - 450&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| DEBUG&lt;br /&gt;
| 600 - 700&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
[[File:Can.drawio.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== DBC File ===&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/blob/master/dbc/project.dbc?ref_type=heads Gitlab DBC File]&lt;br /&gt;
&lt;br /&gt;
Shown below are the messages utilized in our DBC file:&lt;br /&gt;
&lt;br /&gt;
 BO_ 200 SENSOR_SONARS: 4 SENSOR&lt;br /&gt;
  SG_ SENSOR_SONARS_left : 0|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_right : 10|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
  SG_ SENSOR_SONARS_middle : 20|10@1+ (1,0) [6|254] &amp;quot;inch&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 300 MOTOR_CMD: 1 DRIVER&lt;br /&gt;
  SG_ MOTOR_CMD_steer: 0|4@1- (1,0) [-2|2] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
  SG_ MOTOR_CMD_forward: 4|4@1- (1,0) [-5|5] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 310 MOTOR_STOP: 1 SENSOR&lt;br /&gt;
  SG_ MOTOR_STOP_stop: 0|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; MOTOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 400 GPS_NAV: 2 GEO&lt;br /&gt;
  SG_ GPS_NAV_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
  SG_ GPS_NAV_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 420 GPS_HEADING: 4 SENSOR&lt;br /&gt;
  SG_ GPS_HEADING_RAW_DEGREES: 0|16@1+ (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO &lt;br /&gt;
&lt;br /&gt;
 BO_ 430 GEO_STATUS: 4 GEO&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_HEADING : 0|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_COMPASS_BEARING : 5|5@1+ (1,0) [0|18] &amp;quot;Degrees&amp;quot; DRIVER&lt;br /&gt;
   SG_ GEO_STATUS_DISTANCE_TO_DESTINATION : 10|16@1+ (0.1,0) [0|0] &amp;quot;Meters&amp;quot; DRIVER&lt;br /&gt;
&lt;br /&gt;
 BO_ 450 GPS_DESTINATION_LOCATION: 8 DRIVER&lt;br /&gt;
  SG_ GPS_DEST_LATITUDE_SCALED_100000 : 0|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
  SG_ GPS_DEST_LONGITUDE_SCALED_100000 : 32|32@1- (1,0) [0|0] &amp;quot;Degrees&amp;quot; GEO&lt;br /&gt;
&lt;br /&gt;
 BO_ 600 DBG_MSG_MOTOR: 4 MOTOR&lt;br /&gt;
  SG_ SPEED_M_PER_S : 0|32@1+ (0.001,-100) [0|0] &amp;quot;&amp;quot; SENSOR&lt;br /&gt;
&lt;br /&gt;
 BO_ 700 DBG_MSG_GPS: 3 GEO &lt;br /&gt;
  SG_ DBG_GPS_latitude : 0|8@1- (1,0) [-90|90] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_longitude : 8|8@1- (1,0) [-180|180] &amp;quot;&amp;quot; DBG&lt;br /&gt;
  SG_ DBG_GPS_lock : 16|1@1+ (1,0) [0|0] &amp;quot;&amp;quot; DBG&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Sensor and Bridge Controller ECU ==&lt;br /&gt;
[[File:teamxsensors.jpg|500px]] [[File:bluetoothmodule.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/sensor_node?ref_type=heads Gitlab Sensor and Bridge Controller Node Link]&lt;br /&gt;
&lt;br /&gt;
The sensor node utilized the 10Hz to handle message transmission and reception. Meanwhile, the 1Hz periodics receives the sensor data directly from the utrasonic sensor modules. (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  if (callback_count == 0) {&lt;br /&gt;
    delay__us(450000); // start up delay&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  ultra_sonic_sensor_data_s data;&lt;br /&gt;
  ultrasonic_sensor__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  sensors__transmit_once();&lt;br /&gt;
  bridge_get_data();&lt;br /&gt;
  sensor_msg_bank__handle_msg();&lt;br /&gt;
  heading_sensor_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor Modules ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ultrasonic_sensor__run_once() {&lt;br /&gt;
  // left, middle, right respectively&lt;br /&gt;
  for (int i = 0; i &amp;lt; 8; i++) {&lt;br /&gt;
    uint16_t adc_raw[3];&lt;br /&gt;
&lt;br /&gt;
    // start range and wait till all sensors are done&lt;br /&gt;
    ultrasonic_range_once();&lt;br /&gt;
&lt;br /&gt;
    adc_raw[0] = adc__get_adc_value(ADC__CHANNEL_2);&lt;br /&gt;
    adc_raw[1] = adc__get_adc_value(ADC__CHANNEL_4);&lt;br /&gt;
    adc_raw[2] = adc__get_adc_value(ADC__CHANNEL_5);&lt;br /&gt;
&lt;br /&gt;
    // printf(&amp;quot;adc2: %d\n&amp;quot;, adc_raw[0]);&lt;br /&gt;
    sensor_data.left = ((float)adc_raw[0] * SCALE_FACTOR);   // / 2.0;&lt;br /&gt;
    sensor_data.middle = ((float)adc_raw[1] * SCALE_FACTOR); // / 2.0;&lt;br /&gt;
    sensor_data.right = ((float)adc_raw[2] * SCALE_FACTOR);  // / 2.0;&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.left &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led3());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led3());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.middle &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led2());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led2());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (sensor_data.right &amp;lt; 36) {&lt;br /&gt;
      gpio__reset(board_io__get_led1());&lt;br /&gt;
    } else {&lt;br /&gt;
      gpio__set(board_io__get_led1());&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Bridge Controller ==== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_get_data(void) {&lt;br /&gt;
  char byte;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(bridge_uart, &amp;amp;byte, zero_timeout)) {&lt;br /&gt;
    printf(&amp;quot;received byte: %c\n&amp;quot;, byte);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void bridge_send_data(void *buf, uint8_t buf_size, uint8_t msg_id) {&lt;br /&gt;
  // Format the float value from the buffer&lt;br /&gt;
  char tx_buf[30];&lt;br /&gt;
  if (msg_id == 0) {&lt;br /&gt;
    float speed = *(float *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;spd: %f\n&amp;quot;, (double)speed);&lt;br /&gt;
  } else if (msg_id == 1) {&lt;br /&gt;
    dbc_GEO_STATUS_s *msg = (dbc_GEO_STATUS_s *)buf;&lt;br /&gt;
    snprintf(tx_buf, sizeof(tx_buf), &amp;quot;hd: %d, br: %d\n&amp;quot;, msg-&amp;gt;GEO_STATUS_COMPASS_HEADING,&lt;br /&gt;
             msg-&amp;gt;GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
  }&lt;br /&gt;
  uart_printf(bridge_uart, &amp;quot;%s&amp;quot;, tx_buf);&lt;br /&gt;
  // printf(&amp;quot;sent: %s\n&amp;quot;, tx_buf);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void process_uart_data_buf(void) {&lt;br /&gt;
  int temp;&lt;br /&gt;
  char *token;&lt;br /&gt;
  int coord_received = 0;&lt;br /&gt;
&lt;br /&gt;
  token = strtok(uart_rx_buf, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_latitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;la: %f &amp;quot;, (double)dest_latitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
  temp = atoi(token);&lt;br /&gt;
  if (temp != 0) {&lt;br /&gt;
    dest_longitude = temp / 1000000.0f;&lt;br /&gt;
    printf(&amp;quot;lo: %f\n&amp;quot;, (double)dest_longitude);&lt;br /&gt;
    coord_received++;&lt;br /&gt;
  }&lt;br /&gt;
  if (coord_received == 2) {&lt;br /&gt;
    dbc_GPS_DESTINATION_LOCATION_s dest_location;&lt;br /&gt;
    dest_location.GPS_DEST_LATITUDE_SCALED_100000 = dest_latitude * 100000;&lt;br /&gt;
    dest_location.GPS_DEST_LONGITUDE_SCALED_100000 = dest_longitude * 100000;&lt;br /&gt;
    can__msg_t can_msg = {};&lt;br /&gt;
    dbc_message_header_t header;&lt;br /&gt;
    header = dbc_encode_GPS_DESTINATION_LOCATION(can_msg.data.bytes, &amp;amp;dest_location);&lt;br /&gt;
&lt;br /&gt;
    can_msg.msg_id = header.message_id;&lt;br /&gt;
    can_msg.frame_fields.data_len = header.message_dlc;&lt;br /&gt;
    can__tx(can1, &amp;amp;can_msg, 0);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
Our main problems with the sensors occurred once we began outdoor testing. When testing indoors, we encountered no issues with the sensors incorrectly detecting obstacles. However, when moving outdoors, the sensors would detect the ground; thus, we resolved the issue by re-mounting the sensors at an angle in order to avoid detecting the ground. Additionally, we found that when initially calibrating the sensors on startup, there must not be any objects in its way for proper calibration, otherwise we would run into undefined behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Motor ECU ==&lt;br /&gt;
[[File:Motormodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/motor_node?ref_type=heads Gitlab Motor Node Link]&lt;br /&gt;
&lt;br /&gt;
The motor node utilized the 10Hz to handle message transmission and reception as well as for setting the PWM value. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_msg_bank__handle_msg();&lt;br /&gt;
  motor_msg_bank__service_mia_10hz();&lt;br /&gt;
  motor_pwm__set_pwm();&lt;br /&gt;
  if (callback_count % 7 == 0) {&lt;br /&gt;
    motor_rpm_driver_calc_speed();&lt;br /&gt;
  }&lt;br /&gt;
  motor_speed_pid_run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  motor_pwm__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
The motor is directly connected to the RC car's ESC device, which is what initially controlled the car's PWMs prior to hijacking. The SJ2 now becomes the PWM transmitter as opposed to the remote and sends the corresponding PWMs based on driver signals (see Software Design for more details)&lt;br /&gt;
&lt;br /&gt;
[[File:motornode.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
The motor software takes in driver-motor commands through the CAN bus and based on those commands, modifies the PWMs of the motor and the servo to move the wheels forward, backward, left, or right. We mapped the PWM values (See tables below). &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Motor PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 16.2&lt;br /&gt;
| Forward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 14.5&lt;br /&gt;
| Backward&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.02&lt;br /&gt;
| Idle&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;|&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Servo PWM&lt;br /&gt;
! scope=&amp;quot;col&amp;quot;| Direction&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 1&lt;br /&gt;
| 11.0&lt;br /&gt;
| Hard Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 2&lt;br /&gt;
| 13.0&lt;br /&gt;
| Slight Left&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 3&lt;br /&gt;
| 15.48&lt;br /&gt;
| Straight&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 4&lt;br /&gt;
| 17.0&lt;br /&gt;
| Slight Right&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot;| 5&lt;br /&gt;
| 19.0&lt;br /&gt;
| Hard Right&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Additionally, we utilized a general state machine that allowed for fluid transitions between forward and reverse (see diagram below). Without a delay in a neutral state, the car's motor would not be able to easily transition from forward to reverse and vice versa, but would rather just stop. As such, we implemented the diagram in the code below and thus allowed for the car to easily transition between the states of forward and reverse. &lt;br /&gt;
&lt;br /&gt;
[[File:motor_states.png|400px]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void motor_pwm__set_pwm(void) {&lt;br /&gt;
  motor_pwm__set_motor_pwm();&lt;br /&gt;
  motor_pwm__set_servo_pwm();&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_servo_pwm(void) {&lt;br /&gt;
  static float set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_cmd_local.MOTOR_CMD_steer == 0) {&lt;br /&gt;
    // idle&lt;br /&gt;
    set_servo_duty_cycle = servo_pwm_idle_duty_cycle;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 1) {&lt;br /&gt;
    // slightly right&lt;br /&gt;
    set_servo_duty_cycle = 17.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == 2) {&lt;br /&gt;
    // max right&lt;br /&gt;
    set_servo_duty_cycle = 19.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -1) {&lt;br /&gt;
    // slightly left&lt;br /&gt;
    set_servo_duty_cycle = 13.00;&lt;br /&gt;
  } else if (motor_cmd_local.MOTOR_CMD_steer == -2) {&lt;br /&gt;
    // max left&lt;br /&gt;
    set_servo_duty_cycle = 11.00;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  pwm1__set_duty_cycle(servo_pwm, set_servo_duty_cycle);&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void motor_pwm__set_motor_pwm(void) {&lt;br /&gt;
  static float set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
&lt;br /&gt;
  if (motor_stop_local.MOTOR_STOP_stop) {&lt;br /&gt;
    motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
  } else {&lt;br /&gt;
    if (!ignore_cmds) {&lt;br /&gt;
      if (motor_cmd_local.MOTOR_CMD_forward == -1) { // Reverse&lt;br /&gt;
        set_motor_duty_cycle = 14.50;&lt;br /&gt;
        if (reverse_flag == false) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = true;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 0) { // Stop&lt;br /&gt;
        set_motor_duty_cycle = motor_pwm_idle_duty_cycle;&lt;br /&gt;
      } else if (motor_cmd_local.MOTOR_CMD_forward == 3) { // Forward&lt;br /&gt;
        set_motor_duty_cycle = 16.2;                       // 15.7 originally?, 16.2&lt;br /&gt;
&lt;br /&gt;
        if (reverse_flag == true) {&lt;br /&gt;
          motor_speed_pid_set_target_pwm(motor_pwm_idle_duty_cycle);&lt;br /&gt;
          ignore_cmds = true;&lt;br /&gt;
          start_time = sys_time__get_uptime_ms();&lt;br /&gt;
          reverse_flag = false;&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      motor_speed_pid_set_target_pwm(set_motor_duty_cycle);&lt;br /&gt;
    } else if (sys_time__get_uptime_ms() - start_time &amp;gt;= DIRECTION_CHANGE_DELAY) {&lt;br /&gt;
      ignore_cmds = false;&lt;br /&gt;
    } else {&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
The main issue we had with the motor controller occurred during PWM hacking because we did not want the motor running the car too fast. As such, we had to guess and check a good PWM value to run the motor so that the car would move at the slowest speed possible without running into issues where it could not move. However, with continual tests, we managed to identify the ideal range we wanted to run the motor.&lt;br /&gt;
&lt;br /&gt;
Additionally, we encountered minor issues where, when transitioning from reverse to forward and vice versa, the car would go into neutral and never exit from the neutral state. We found that this issue occurred due to the delay being too small, and so the motor would not have enough time to truly go into neutral and thus would remain in that state. To counter this, we increased the delay and also slightly increased the forward PWM in order to combat the potential that it was stopping due to having too low of a PWM value. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Geographical Controller ==&lt;br /&gt;
&lt;br /&gt;
[[File:Gpsmodule.jpg|400px]] [[File:Compassmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/geo_node?ref_type=heads Gitlab Geo Node Link]&lt;br /&gt;
&lt;br /&gt;
The Geo node utilized the 10Hz to handle message transmission and reception as well as receiving the compass and GPS data directly from the hardware modules. Meanwhile, the 1Hz periodics processed the GPS lock and would toggle the LED on the SJ2 board accordingly (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__1Hz(uint32_t callback_count) {&lt;br /&gt;
  int gps_lock_status = geo_sensors_gps__get_gps_lock_status();&lt;br /&gt;
  // printf(&amp;quot;GPS lock status: %d\n&amp;quot;, gps_lock_status);&lt;br /&gt;
  if (!gps_lock_status) {&lt;br /&gt;
    gpio__toggle(board_io__get_led0());&lt;br /&gt;
  } else {&lt;br /&gt;
    gpio__reset(board_io__get_led0());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
&lt;br /&gt;
  geo_msg_bank__handle_msg();&lt;br /&gt;
  geo_msg_bank__service_mia_10hz();&lt;br /&gt;
  geo_msg_bank__transmit_msg();&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_gps__run_once();&lt;br /&gt;
  // geo_sensors_compass_lsm__run_once();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== GPS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__run_once(void) {&lt;br /&gt;
  bool gps_transfer_success = false;&lt;br /&gt;
&lt;br /&gt;
  gps_transfer_success = geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer();&lt;br /&gt;
  gps__parse_coordinates_from_line();&lt;br /&gt;
&lt;br /&gt;
  return gps_transfer_success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
bool geo_sensors_gps__transfer_data_from_uart_driver_to_line_buffer(void) {&lt;br /&gt;
  char byte_read;&lt;br /&gt;
  const uint32_t zero_timeout = 0;&lt;br /&gt;
  bool gps_status_reading = false;&lt;br /&gt;
&lt;br /&gt;
  while (uart__get(uart_gps, &amp;amp;byte_read, zero_timeout)) {&lt;br /&gt;
    gps_status_reading = true;&lt;br /&gt;
    line_buffer__add_byte(&amp;amp;line, byte_read);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return gps_status_reading;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void gps__parse_coordinates_from_line(void) {&lt;br /&gt;
  char gps_line[1000];&lt;br /&gt;
  if (line_buffer__remove_line(&amp;amp;line, gps_line, sizeof(gps_line))) {&lt;br /&gt;
    // Check if the line is a GPGGA stream&lt;br /&gt;
    if (strncmp(gps_line, &amp;quot;$GPGGA,&amp;quot;, 7) == 0) {&lt;br /&gt;
      char *token;&lt;br /&gt;
      int field_idx = 0;&lt;br /&gt;
      int latitude_flag = 1;&lt;br /&gt;
      int longitude_flag = 1;&lt;br /&gt;
      float latitude = 0.0;&lt;br /&gt;
      float longitude = 0.0;&lt;br /&gt;
&lt;br /&gt;
      token = strtok(gps_line, &amp;quot;,&amp;quot;);&lt;br /&gt;
      while (token != NULL) {&lt;br /&gt;
        token = strtok(NULL, &amp;quot;,&amp;quot;);&lt;br /&gt;
        field_idx++;&lt;br /&gt;
&lt;br /&gt;
        if (field_idx == 2) { // Latitude is field 2 in GPGGA&lt;br /&gt;
          latitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 3) { // N/S direction is field 3 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'S') {&lt;br /&gt;
            latitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 4) { // Longitude is field 4 in GPGGA&lt;br /&gt;
          longitude = atof(token);&lt;br /&gt;
        } else if (field_idx == 5) { // E/W direction is field 5 in GPGGA&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          if (c == 'W') {&lt;br /&gt;
            longitude_flag *= -1;&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx == 6) {&lt;br /&gt;
          char c = token[0];&lt;br /&gt;
          // printf(&amp;quot;GPS QUALITY: %c\n&amp;quot;, c);&lt;br /&gt;
          if (c == '0') {&lt;br /&gt;
            gps_quality = 0;&lt;br /&gt;
          } else if (c == '1') {&lt;br /&gt;
            gps_quality = 1;&lt;br /&gt;
            // printf(&amp;quot;GPS lock successful\n&amp;quot;);&lt;br /&gt;
          } else {&lt;br /&gt;
            // printf(&amp;quot;Trying to acquire GPS fix\n&amp;quot;);&lt;br /&gt;
          }&lt;br /&gt;
        } else if (field_idx &amp;gt; 6) {&lt;br /&gt;
          break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      int latitude_days = latitude / 100;&lt;br /&gt;
      int longitude_days = longitude / 100;&lt;br /&gt;
      float latitude_minutes = latitude - 3700.00;&lt;br /&gt;
      float longitude_minutes = longitude - 12100.00;&lt;br /&gt;
&lt;br /&gt;
      latitude_minutes /= 60;&lt;br /&gt;
      longitude_minutes /= 60;&lt;br /&gt;
&lt;br /&gt;
      parsed_coordinates.latitude = ((1.0 * latitude_days) + latitude_minutes) * latitude_flag;&lt;br /&gt;
      parsed_coordinates.longitude = ((1.0 * longitude_days) + longitude_minutes) * longitude_flag;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Compass Module ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass_lsm__run_once(void) { geo_sensors_compass__read(); }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_sensors_compass__read(void) {&lt;br /&gt;
  uint8_t buffer[6];&lt;br /&gt;
  float mag[3];&lt;br /&gt;
&lt;br /&gt;
  i2c__read_slave_data(current_i2c, magnetometer_read, LSM303AGR_MAG_OUTX_L_REG, buffer, 6);&lt;br /&gt;
&lt;br /&gt;
  mag[0] = (int16_t)(buffer[1] &amp;lt;&amp;lt; 8 | buffer[0]);&lt;br /&gt;
  mag[1] = (int16_t)(buffer[3] &amp;lt;&amp;lt; 8 | buffer[2]);&lt;br /&gt;
  mag[2] = (int16_t)(buffer[5] &amp;lt;&amp;lt; 8 | buffer[4]);&lt;br /&gt;
&lt;br /&gt;
  geo_sensors_compass__calculate_heading(mag);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void geo_sensors_compass__calculate_heading(float mag[3]) {&lt;br /&gt;
  float magnitude = sqrtf(mag[0] * mag[0] + mag[1] * mag[1] + mag[2] * mag[2]);&lt;br /&gt;
&lt;br /&gt;
  const float epsilon = 1e-6;&lt;br /&gt;
  if (magnitude &amp;lt; epsilon) {&lt;br /&gt;
    current_compass_heading = 0;&lt;br /&gt;
&lt;br /&gt;
  } else {&lt;br /&gt;
    float mag_xy = mag[1] / magnitude;&lt;br /&gt;
    float mag_xx = mag[0] / magnitude;&lt;br /&gt;
    current_compass_heading = (atan2(mag_xy, mag_xx)) * 180.0 / PI;&lt;br /&gt;
&lt;br /&gt;
    if (current_compass_heading &amp;lt; 0) {&lt;br /&gt;
      current_compass_heading = 360 + current_compass_heading;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Haversine Algorithm ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang = &amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void geo_logic__haversine_calculation(void) {&lt;br /&gt;
  // Input: Latitude longitude, Output: heading, bearing, distance to destination&lt;br /&gt;
  gps_coordinates_t current_gps = geo_sensors_gps__get_coordinates();&lt;br /&gt;
  float current_latitude = current_gps.latitude;&lt;br /&gt;
  float current_longitude = current_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  gps_coordinates_t destination_gps = geo_msg_bank__get_gps_destination_location();&lt;br /&gt;
  float destination_latitude = destination_gps.latitude;&lt;br /&gt;
  float destination_longitude = destination_gps.longitude;&lt;br /&gt;
&lt;br /&gt;
  // Convert latitudes and longitudes from degrees to radians&lt;br /&gt;
  float phi1 = (float)(current_latitude * (float)PI_VAL / (float)180.0);&lt;br /&gt;
  float phi2 = (float)(destination_latitude * PI_VAL / (float)180.0);&lt;br /&gt;
  float delta_phi = (float)(destination_latitude - current_latitude) * PI_VAL / (float)180.0;&lt;br /&gt;
  float delta_lambda = (float)(destination_longitude - current_longitude) * PI_VAL / (float)180.0;&lt;br /&gt;
&lt;br /&gt;
  // Haversine formula&lt;br /&gt;
  float a =&lt;br /&gt;
      sin(delta_phi / 2) * sin(delta_phi / 2) + cos(phi1) * cos(phi2) * sin(delta_lambda / 2) * sin(delta_lambda / 2);&lt;br /&gt;
  float c = 2 * atan2(sqrt(a), sqrt(1 - a));&lt;br /&gt;
  distance_to_destination = RADIUS_EARTH_KM * c * 1000;&lt;br /&gt;
&lt;br /&gt;
  // Bearing calculation&lt;br /&gt;
  float y = sin(delta_lambda) * cos(phi2);&lt;br /&gt;
  float x = cos(phi1) * sin(phi2) - sin(phi1) * cos(phi2) * cos(delta_lambda);&lt;br /&gt;
  float bearing_rad = atan2(y, x);&lt;br /&gt;
  compass_bearing = fmod((bearing_rad * (float)180.0 / (float)PI_VAL + (float)360.0), 360.0);&lt;br /&gt;
&lt;br /&gt;
  compass_heading = geo_msg_bank__get_heading();&lt;br /&gt;
&lt;br /&gt;
  current_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION = distance_to_destination;&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_BEARING = (uint8_t)(compass_bearing / 20);&lt;br /&gt;
  current_geo_status.GEO_STATUS_COMPASS_HEADING = (uint8_t)(compass_heading);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of our problems occurred due to an inaccurate compass reading. Mainly, the compass heading angles would not return as very stable values, which resulted in unstable driving behavior for the car. We initially thought it would resolve itself with some software calibration; however, we found that even with software calibration for the LSM, it would still be inaccurate and would not even give the full 360 degree heading range. As such, we resolved to simply buying a more expensive and more accurate magnetometer, which solved our issues. The new module returned more stable values, despite it still fluctuating, the angle changing was not enough to affect the driving behavior since we built our module with the intent of not needing the exact degree of the angles. We also found some trouble mounting the compass due to not wanting to have to handle any further offsets after mounting. Because of this, when mounting the compass, we identified where the compass was detecting north and faced the front of the car in that direction. Following that step, we mounted the compass so that it, along with the front of the car, was facing north.&lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into some errors with the GPS module due to us receiving latitude and longitude values that were miles away from our current location. However, we identified an error on our end where we had misinterpreted the data returned from the GPS module and thus were calculating the wrong latitude and longitude values. Once we identified the error, we found the correct way to extract the correct latitude and longitude values for a more accurate location. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Driver and LCD Module ==&lt;br /&gt;
[[File:LCDmodule.jpg|500px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/projects/driver_node?ref_type=heads Gitlab Driver &amp;amp; LCD Node Link]&lt;br /&gt;
&lt;br /&gt;
The driver node utilized the 10Hz to handle message transmission and reception as well as setting the LCD display for status updates and motor commands. Meanwhile, the 100Hz periodics processed the inputs and transferred the data received to their respective local data structures (see Software Design for more details).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__10Hz(uint32_t callback_count) {&lt;br /&gt;
  driver_msg_bank__handle_msg();&lt;br /&gt;
  driver_msg_bank__service_mia_10hz();&lt;br /&gt;
  driver_msg_bank__transmit_msg();&lt;br /&gt;
  i2c_lcd__clear();&lt;br /&gt;
  driver__lcd_display_data();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void periodic_callbacks__100Hz(uint32_t callback_count) {&lt;br /&gt;
  driver__process_input();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hardware Design ===&lt;br /&gt;
The driver node, aside from its connection to the CAN bus, is also connected the the LCD module through I2C. the SJ2 board provides the LCD with 3.3V power and sends data through SDA and SCL.&lt;br /&gt;
&lt;br /&gt;
[[File:driverLCD.png|500px]]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&lt;br /&gt;
==== Driver Logic ====&lt;br /&gt;
The driver logic came down to a basic logic (shown in table below) where 0 implies there is no obstacle, and 1 implies an obstacle is present. Depending on each combination of 1's and 0's, the car would determine whether to turn left or right and move forward or backward.&lt;br /&gt;
&lt;br /&gt;
[[File:driverlogic.png|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void driver__avoid_obstacle(void) {&lt;br /&gt;
  obstacle_below_threshold_s below_thresholds = driver__closest_obstacle();&lt;br /&gt;
&lt;br /&gt;
  if (!(below_thresholds.MIDDLE) &amp;amp;&amp;amp; !(below_thresholds.LEFT) &amp;amp;&amp;amp; !(below_thresholds.RIGHT)) {&lt;br /&gt;
    driver__go_to_destination();&lt;br /&gt;
  } else {&lt;br /&gt;
    driver__steering();&lt;br /&gt;
  }&lt;br /&gt;
  driver__led_indicators();}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
dbc_MOTOR_CMD_s driver__get_motor_commands(void) {&lt;br /&gt;
  driver__avoid_obstacle();&lt;br /&gt;
  return motor_commands;&lt;br /&gt;
}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// slight turns for destination movement&lt;br /&gt;
static void driver__go_to_destination(void) {&lt;br /&gt;
  int angle_difference = local_geo_status.GEO_STATUS_COMPASS_HEADING - local_geo_status.GEO_STATUS_COMPASS_BEARING;&lt;br /&gt;
  if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;gt; 0) {&lt;br /&gt;
    if (angle_difference &amp;gt; 0) {&lt;br /&gt;
      // go right&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else if (angle_difference &amp;lt; 0) {&lt;br /&gt;
      // go left&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = -1;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    } else { // go straight&lt;br /&gt;
      motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
      motor_commands.MOTOR_CMD_forward = 3;&lt;br /&gt;
    }&lt;br /&gt;
  } else { // reached destination&lt;br /&gt;
    motor_commands.MOTOR_CMD_forward = 0;&lt;br /&gt;
    motor_commands.MOTOR_CMD_steer = 0;&lt;br /&gt;
  }}&lt;br /&gt;
  &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LCD Logic ====&lt;br /&gt;
&lt;br /&gt;
The LCD printout relied solely on the datasheet. However, some optimizations were made in order to simplify the LCD methods. Specifically the starting_position() function allowed for eash access to each line of the LCD's printout without needing to write data to the addresses every time we wanted to print onto a specific line. Additionally, the lcd__printf() was a minor optimization to allow for us to print a string rather than per character and calling the send_data() function for every character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void driver__lcd_display_data(void) {&lt;br /&gt;
  char debug_str[15];&lt;br /&gt;
&lt;br /&gt;
  if (local_motor_stop.MOTOR_STOP_stop) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   MOTOR STOPPED!&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot; PLS RESTART BRIDGE&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     TO CONTINUE&amp;quot;);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION) {&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%.1lf&amp;quot;, local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(0, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;   PROJECT TEAM X&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;Distance:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(1, 10);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_HEADING);&lt;br /&gt;
    i2c_lcd__starting_position(2, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Heading:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(2, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
&lt;br /&gt;
    sprintf(debug_str, &amp;quot;%d&amp;quot;, (int)local_geo_status.GEO_STATUS_COMPASS_BEARING);&lt;br /&gt;
    i2c_lcd__starting_position(3, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;     Bearing:&amp;quot;);&lt;br /&gt;
    i2c_lcd__starting_position(3, 13);&lt;br /&gt;
    i2c_lcd__printf(debug_str);&lt;br /&gt;
  } else if (local_geo_status.GEO_STATUS_DISTANCE_TO_DESTINATION &amp;lt; 2) {&lt;br /&gt;
    i2c_lcd__clear();&lt;br /&gt;
    i2c_lcd__starting_position(1, 0);&lt;br /&gt;
    i2c_lcd__printf(&amp;quot;DESTINATION REACHED!&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__printf(char *line) {&lt;br /&gt;
  while (*line)&lt;br /&gt;
    i2c_lcd__send_data(*line++);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void i2c_lcd__starting_position(uint8_t y, uint8_t x) {&lt;br /&gt;
&lt;br /&gt;
  char Starting_Addr = 0x80;&lt;br /&gt;
  switch (y) {&lt;br /&gt;
  case 0:&lt;br /&gt;
    Starting_Addr |= 0x00;&lt;br /&gt;
    break;&lt;br /&gt;
  case 1:&lt;br /&gt;
    Starting_Addr |= 0x40;&lt;br /&gt;
    break;&lt;br /&gt;
  case 2:&lt;br /&gt;
    Starting_Addr |= 0x14;&lt;br /&gt;
    break;&lt;br /&gt;
  case 3:&lt;br /&gt;
    Starting_Addr |= 0x54;&lt;br /&gt;
    break;&lt;br /&gt;
  default:;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  Starting_Addr += x;&lt;br /&gt;
&lt;br /&gt;
  i2c_lcd__send_command(0x80 | Starting_Addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
Most of the issues surrounding the driver node occurred after field testing. We found there were some issues regarding how soon the driver made the decision compared to when it received the sensor data. As such, we made adjustments so that, despite the delay, there would be time for the driver to receive the sensor data, identify the correct action to take, and then move the wheels. Overall, our driver node worked as expected once we started field testing due to the multitude of unit tests that were done to ensure proper logic. We also identified some minor issues when testing the reverse; however, this was easily fixed by modifying the sensor priorities so that the middle sensor's values, when below the threshold, had the most precedence. In this way, the car would not fluctuate between going forward and reverse when there was clearly an object in front of it. &lt;br /&gt;
&lt;br /&gt;
Additionally, we ran into issues integrating the LCD screen through I2C. The screen's slave address would not get detected, despite the device having the proper connections and power supply. However, it was found to have just been a hardware issue and worked with a different I2C LCD device. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mobile Application ==&lt;br /&gt;
&amp;lt;Picture&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master/mobile_app?ref_type=heads Gitlab Mobile App Node Link ]&lt;br /&gt;
&lt;br /&gt;
=== Software Design ===&lt;br /&gt;
&amp;lt;List the code modules that are being called periodically.&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Technical Challenges ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt; List of problems and their detailed resolutions&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;HR&amp;gt;&lt;br /&gt;
&amp;lt;BR/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion ==&lt;br /&gt;
Overall, through the course of this project, we learned a lot of different types of skills. Starting with the obvious, we learned better coding habits such as code modularization and code decoupling. We also understood how to utilize the CAN bus and messages in addition to PWM hacking for the RC car. In the scope of the project, we learned communication and group coordination and distribution skills. Although we had a bit of a rough start, as the project continued, we better understood how to organize weekly goals and tasks that needed to be done and properly distributed these tasks to each member. In addition, we learned how to collaborate with each other to build the RC car.&lt;br /&gt;
&lt;br /&gt;
=== Project Video ===&lt;br /&gt;
&lt;br /&gt;
=== Project Source Code ===&lt;br /&gt;
&lt;br /&gt;
[https://gitlab.com/ashley.n.ho/sjtwo-c/-/tree/master?ref_type=heads Team X Gitlab Link ]&lt;br /&gt;
&lt;br /&gt;
=== Advice for Future Students ===&lt;br /&gt;
* Start early! Order things as soon as you have your team, and keep track of finances for reimbursements&lt;br /&gt;
* Unit-testing lets you know the obstacle avoidance works the way you generally want it to and saves a lot of time when field testing&lt;br /&gt;
* Try to keep the logic simple, over-complicating the code modules makes your debugging time a lot harder since you won't know where things went wrong&lt;br /&gt;
&lt;br /&gt;
=== Acknowledgement ===&lt;br /&gt;
&lt;br /&gt;
Thank you Preet for the class and for guiding us through the process of building the RC car.&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
1. [http://www.handsontec.com/dataspecs/I2C_2004_LCD.pdf  LCD Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
2. [https://controllerstech.com/lcd-20x4-using-i2c-with-stm32/ LCD Display Initialization and Configuration Example Tutorial]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;br /&gt;
3. [https://maxbotix.com/pages/lv-maxsonar-ez-datasheet Ultrasonic Sensors Datasheet]&lt;br /&gt;
&amp;lt;BR&amp;gt;&lt;/div&gt;</summary>
		<author><name>243 user3</name></author>	</entry>

	</feed>