Introduction: Digital Compass and Heading Finder
California Maritime Academy
The basis of this project is a digital compass with heading tracking. This enables the user to follow a heading across long distances using a digital apparatus. Colloquially a heading is an angle measured clockwise from north, which is regarded as zero degrees, as indicated by the compass. The device has two main functions: the first is displaying the current heading of the device on a digital display reference, and the second is the ability to enter a user requested heading, which will be displayed on a ring of LEDs on the top of the compass housing. The user would then adjust the device’s orientation related to the illuminated LED. As the direction of the device is changed, the LED will travel to the center LED, thus indicating that the correct heading has been established.
- DIYmall 6M GPS Module
- HiLetgo MPU9250/6500 9-Axis 9 DOF 16 Bit
- Adafruit NeoPixel Ring 16
- MakerFocus 4pcs 3.7V Lithium Rechargable Battery
- ELEGOO MEGA 2560 R3 Board
- Adafruit Mini Lipo w/Mini-B USB Jack - USB LiIon/LiPoly charger - v1
- 2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket
Step 1: Designing the Functionality of the Project
The first step is understanding the logic and final operational functionality. This logic diagram depicts the three device states and the two sensor states.
State 1: Loading State
The loading state is used to allow the Arduino Mega to get data back from the two sensors upon start up. The device will display loading on the screen, clear all the number values on the screen, and the LEDs on the NeoPixel ring will light in a circle.
State 2: Compass Mode
In this state the device will act like a digital compass. The NeoPixel ring will light up to indicate the direction of North with respect to the orientation of the device. The true device heading will also be displayed on the LCD screen along with the Latitude and Longitude of the device. It will also be within this state that the user will be able to enter the user heading to be displayed in State 3.
State 3: Heading Tracking Mode
In this state the device will now help the user become established on their desired heading. The device will now display the devices heading and the users heading on the LCD screen along with the latitude and longitude data. The NeoPixel ring will now light up to indicate the users heading with respect to the devices orientation.
Within both State 2 and State 3 there are two sensor states these sensor states allow the device to pull data from the sensor that provides the most accurate data depending on the operational condition of the device.
Sensor State 1: MPU
If the device is not moving the heading data will be pulled from the MPU as it is the most accurate data when the device is not moving.
Sensor State 2: GPS
If the device is moving the heading data will be pulled from the GPS chip as it is the most accurate data in this condition.
The device can switch between these to sensor states at any time to account for the use conditions of the unit changing. This is important to the operation of the device as both of the two sensors used in the device have conditions that effect the accuracy of the data they provide. In the case of the MPU the chip can easily be influenced by local magnetic fields caused by cars and metal construction materials in buildings. Thus a GPS chip is used that can provide a much more accurate heading that is not effected by the same influences. However, the GPS can only provide heading data when moving as it calculates heading using the change in latitude and longitude data. Therefore the chips complement each other and by using the two sensor states provide the most accurate and reliable device functionality.
Step 2: Setup and Wire Diagram
The project utilizes and Arduino Mega clone board similar to the board above. All components in the project will be connected to this board. Above are detailed diagrams of how to wire up the components for this project. The buttons do not have a detailed circuit as these can be set up many ways. In this project they use a 100K pull down resistor and a simple button to send a 3 volt signal to its assigned pin.
Step 3: Testing Components and Basic Code
The project will pull data from both the MPU and GPS chip as described earlier. Attached are three codes that enable testing of data from the MPU, GPS, and MPU with screen to verify functionality of the parts. It is important to get the components operational in this stage as the code is separate for each chip and any issues can be addressed without fear of causing unforeseen errors in the final code.
All of these can be found by searching the titles above. I will not be posting links as there are many copies of these libraries from multiple sources and adhering to the community standard of only linking to originals I will let you find these for yourself.
Step 4: MPU Calibration
The heading found via the MPU in State 2 and State 3 was split up into four quadrants. This was necessary because our method of calibration required finding the minimum and maximum magnitudes from the magnetometer along its x and y axes. This was done through rotating the device randomly about its three axes, free from any significant electromagnetic fields other than that of the Earth. We then took the minimum and maximum values along the x and y axis and plugged them into a scaling equation in order to restrict the magnitudes between the values of negative one and one. In the above figure, BigX and BigY are the maximum values of magnetometer data along the x and y-axis respectively, LittleX and LittleY are the minimum values of magnetometer data along the x and y-axis respectively, IMU.getMagX_uT() and IMU.getMagY_uT() are the values being pulled from the magnetometer at any time along the x and y-axis respectively, and Mx and My are the new scaled values used to calculate the heading.
Step 5: Final Code
The final step is to create the final code. I have attached a copy of the projects final code. Within notes have been made to help navigate the code. The biggest challenge of this section was getting the quadrants to work correctly. The implementation of quadrants proved to be more tedious and logic-defying than we could have ever anticipated. We initially implemented a basic arctan(My/Mx) and then converted from radians to degrees, since Arduino outputs in radians by default. However, the only quadrant this worked in was from 90 degrees to 180 degrees, which gave us a negative output and ended up being Quadrant III. The solution to this was taking the absolute value, since it still incremented correctly. This value was then subtracted from 360 to light up the correct NeoPixel LED in state 2 and a similar mathematical operation was used in state 3 based on if the heading was larger or smaller than the user’s input heading, both of which can be seen in the above code. In the above figures, Heading corresponds to the NeoPixel light that will be lit up based on the difference between the device heading and the deviation from north in the case of state 2, and from that of the user heading. In this case, 90 to 180 degrees corresponds to Quadrant III. In both cases, the tft.print causes the screen to read the device heading from north.
For the other three quadrants, implementation of arctan(My/Mx) led to an inversion of incrementation as the device was rotated, i.e. the heading angle would count down when it was supposed to count up and vice versa. The solution to this problem was to flip the arctangent to the form of arctan(Mx/My). While this solved the incrementation inversion, it did not give the correct device heading, which is where the quadrants came into play. The simple fix to this was to add a shift based on the corresponding quadrant. This can be seen in the following figures, which are once again pieces of code from States 2 and 3 of each quadrant.
The first if statement is carried out if the heading calculated by the MPU equation is greater than the user heading. Under this condition the user’s input heading is added to the device heading and the corresponding value is subtracted from 360. If the else statement is carried out, the MPU heading equation is subtracted from the user’s input heading. These conditions were implemented in order to not only get an accurate value for the NeoPixel, but to avoid obtaining a value outside of the acceptable range, which is from 0 to 359 degrees.