The accelerometer and gyroscope sensors provide the core data behind the user's relative motion. The
accelerometer that we used was the Adafruit ADXL 335. This particular sensor output analog voltages for all 3-axis: X,
Y,Z. These analog voltages were read into the microcontroller using the built-in ADC10. The x-direction corresponded
to analog pin 4 (AN4) or physical pin 6. The y-direction corresponded to analog pin 5 (AN5) or physical pin 7. The z-
direction corresponded to analog pin 11 (AN11) or physical pin 24. This enabled us to gather acceleration data and
mapped it to a range from 0-1024 corresponding to a voltage from 0 (Low) to 3.3 (High). In order to find the
acceleration due to gravity, we tried to measure the force of gravity along each axis. This required initial calibration in
order to average out the accelerometer's measurements and also closely correlate the accelerometer data with the
gyroscope. The Gyroscope that we used was also from Adafruit. The specific device that we used was the L3GD20H,
a MEMS motion sensor three-axis digital output gyroscope. We used this device to gather data about rotation about all
of the 3-axis in units of DPS (degrees per second). We retrieved data from this sensor using the SPI2 channel with the
standard 4-wire SPI protocol that we mentioned above. It is extremely important to note that the serial data out (SDO)
pin from the microcontroller connected to the serial data in (SDI) pin on the slave devices, in this case the gyroscope.
The clock line was the standard PIC32 SCK2 or physical pin 26. The data out (MOSI) line we chose to be RPA4 or
physical pin 12 while the data in line (MISO) we chose to be RPA2 or physical pin 9 on the microcontroller. The chip
select for the gyroscope was RPB5 or physical pin 14. The combination of the accelerometer and the gyroscope
formed our attempt at synthesizing our own inertial measurement unit.
Pressure and temperature were one of the first things that we wanted to implement and existed from the original
project idea. By having an accurate measure of these statistics, we knew we could accurately show accurate altitude
changes as well as weather shifts during a ski run down a mountain. This could also help us analyze our vertical
displacement rate or downward velocity.
The pressure sensor that we used was the Adafruit BMP 183. This sensor we also communicated with via SPI
channel2. As we mentioned earlier, this sensor shared the SPI channel with the gyroscope, but this didn't matter
because both had unique chip select pins. The chip select pin for the pressure sensor was RPB7 or physical pin 16.
The pressure sensor outputted temperature and pressure data which we then used to calculate altitude.
The BMP183 was designed to be connected directly to a microcontroller by the SPI bus. The pressure and
temperature data had to be compensated with built-in calibration unique to each device. This "E^2PROM" data was
initialized before data acquisition to properly calculate the pressure and temperature from the raw inputs. This
specific device consisted of a piezo-resistive sensor, an analog to digital converter and a control unit with the
calibration data registers and an SPI interface.
The idea of incorporating an external memory space was not in the intial plans for our project. However, as we
shifted over to a logger device, we knew that a MicroSD would be perfect to save and log performance data for later
analysis. In order to communicate with the MicroSD card and store our data, we needed a breakout board. The one
that we used was from Sparkfun. We decided against using the MicroSD on the TFT because we wanted to save
money and also eliminate the display because we wanted the device to be discrete and also for safety reasons for
possible distractions when you are skiing or doing active outdoor sports. The MicroSD breakout ran using the SPI1
channel and used the following pins: SCK1 (pin 25) was the clock, RPB9 (pin 18) was the CS, RPB11(pin 22) was
the MOSI, RPB8 (pin 17) was the MISO, and finally RPB10 (pin 21) was the CD or chip detect. The chip detect pin
would check whether or not a MicroSD was inserted into the card holder. All of the sensors described above were
connected to a common power and ground that came off the Vdd and Gnd pins of the microcontroller.
Throughout the process of building and debugging our device, we relied heavily on the TFT LCD to debug the
particular sensors that we used. However, in the end we decided to remove the TFT from the project because it would
not serve a purpose in data logging and only increased the computing overhead and also for safety considerations
PIC32 Software Overview
The software for this lab was written entirely in the C programming language in the MPLABX IDE. Just as in the
previous laboratories in the ECE 4760 class, the protothreads library was utilized to get data from all the
different sensors and log data onto the microSD card. Four protothread structures were used for each specific
function. One to toggle the ADC and get acceleration data, another to retrieve the temperature, pressure, and
altitude data, another to get the gyroscope readings for each axis, and finally one to save the most current data
to the microSD card in a ".csv" file format. The specific functions and complementary files are discussed next.
Analog to Digital Conversion
In order to read the analog inputs from the accelerometer sensor, we had to set up and use the built-in analog
to digital converter, ADC10. We wanted to output in integer values between 0 and 1023 and chose to enable
autosampling. we used the peripheral bus clock (PBClock). Additionally, we set the three analog input pins that we
were using to measure the x,y, and z vectors of acceleration. In our main function, we opened and enabled the
ADC10 module. We then used the Protothreads library provided from "pt_cornell_1_2.h" in order to have a separate
acceleration thread that simply measured each value sequentially. In the thread, we first set the channel using the
SetChanADC10() to the first analog input pin, AN4 that corresponded to the x-direction of
acceleration. We then yielded the thread for 2 milliseconds to allow for the channel to be set correctly. Next, we read
the value of the analog pin using the function
ReadADC10() and set the value to the corresponding
static variable. The last piece of the code that we used was a microsecond timer for more precise time measurements
in order to integrate the acceleration to find the velocity. This was achieved through a Timer2 interrupt that
incremented a variable
time_microsec every microsecond. This integration proved to produce large
accumulating errors in often varying functions as we will explain later, so this technique was abandoned in favor of
post-processing numerical integration in the python script.
Left: X-axis acceleration analog input. Right: X(top) and Y(bottom)-axis
acceleration analog inputs.
Please right click and open images in new tab for better viewing.
The above graphs show the analog input voltages from the X and Y data out lines from the accelerometer during
a test movement. The first graph on the left shows just the X-axis acceleration. As we can see, steady state no
movement produces a fairly noisy signal. We tried to correct for this with a simple passive low pass filter, but it did
not produce a significant improvement. The positive edge spikes correspond to an acceleration in the positive x-axis
direction, while the following negative spikes correspond to decelerating by the same amount. This makes sense
because we tested them by quickly snapping them in one direction with our hands and then stopping so the
acceleration to produce the initial snap was then immediately followed by a deceleration to stop the movement. In
addition, it is important to note that orthogonal axis were fairly well isolated from each other. This is shown from the
X and Y axis outputs from the graph on the right.
As we can also see from the screenshots above, the value of the x-axis accelerometer analog input was about
1.5-1.6V which is a little smaller than half of the reference 3.3V. This means that for any axis orthogonal to the
gravity vector, the analog reading of the accelerometer should fall right in the middle of the low and high reference
voltages. It clearly wasn't exactly 1.65V for each axis so we had to research and calibrate each axis separately.
Since the acceleration of gravity is a constant, we could use it to calibrate our respective 3-axis sensitivity. We did
this by measuring the effect of the acceleration of gravity on each axis to find what the output analog voltage was for
+/-1g or +/-9.8m/s^2. This proved helpful in extrapolating our acceleration values to correct units in the post
TO-DO: talk about analog to digital converters and the scale that we used from 0-1023
SPI Data communication
The spi data communication between the microcontroller and the various sensors was essentially the foundation
of data collection for our project. Each sensor, the pressure and the gyroscope as well as the sd card breakout had
a different spi protocol. The majority of the lab was focused on building the integration of software for communicating
with each of these external boards by SPI.
The first board we worked with was the Adafruit BMP183 pressure sensor. This sensor came with plenty of
documentation on how to setup and receive data. Because we had both the pressure sensor as well as the gyroscope
communicating by the SPI2 channel, we first initialized the channel in the main function before beginning any
pressure calibrations. The SPI channel was configured to run at 2MHz because this produced the least noisy clock
and data waveforms to ensure accurate data (both devices had a maximum clock frequency of 10MHz). All of the
SPI functionality for the pressure sensor was written in the corresponding header and source files:
pressure.c. The header file initializes all of the registers that we
communicate with. The 16-bit register addresses of calibration data start from address 0xAA and go to address 0xBF.
We also initialize the Mode Settings that the sensor will be running in. This mode setting is passed as an input to the
pressure_begin(unsigned char mode) function. The purpose of this mode is to set the accuracy
settings for the pressure reading. The table for the different modes with following delay times is below
BMP183 Hardware Accuracy Modes
Please right click and open images in new tab for
Lastly, the header file initializes the pressure chip select pin as RPB7 as well as all of the E^2PROM constants
variables and functions that we use in the
pressure.c source file. These functions are listed and
void pressure_begin(unsigned char); - Sets the oversampling mode and reads out all of
the E^2PROM calibration data.
void spiwrite(unsigned char address, unsigned char data); - Writes the 2-Byte(16 bit)
data to the corresponding address.
unsigned char spiread8(unsigned char address); - Reads the 1-Byte(8-bit) data from the
corresponding address input.
unsigned int spiread16(unsigned char address); - Reads the 2-Byte(16-bit) data from the
corresponding address input.
void delayMs(unsigned long ms); - Creates a millisecond software delay to allow for the
conversion time in oversampling setting modes.
void read_calibration_data(void); - Reads the E^2PROM calibration data and initializes all
of the constants for the specific device used in the temperature and pressure calculations.
UINT16 read_raw_temperature(void); - This function first writes the command to read the
temperature to the control register, 0xF4. We delay for the conversion time and then read the temperature data
UINT32 read_raw_pressure(void); - This function first writes the command to read the
pressure to the control register, 0xF4. We then delay based on the oversampling mode defined as the conversion
times seen above and then read the pressure data as 3-bytes in order to ensure precision in the high resolution
modes. In the lowest resolution mode, the pressure data is represented by 16-bits while in the highest resolution
modes, the pressure is represented with 19-bits.
float get_temperature(void); - Returns the temperature in degrees Celsius as a float.
INT32 get_pressure(void); - Returns the pressure in Pascals.
float get_altitude(void); // std atmosphere- Returns the altitude form the calculated
pressure. The calculation and relation of altitude and pressure is shown below.
Pressure-Altitude Relation and Barometric Sea Level Pressure Worldwide
It is worth noting that in order to get an accurate absolute calculation of the altitude, the barometric sea level
pressure is necessary. This value was assumed to be a constant 101325 Pascals. However, in real life this value
is not a constant. In fact, it is changing every single day. One way that we could have improved this project would
have been to include an input to the system that referred to the local sea level pressure for the specific day. The
below graph shows the variation in sea level pressure averages over the entire globe. Clearly, assuming a constant
value would produce different absolute results all over the planet. A positive aspect of the pressure and altitude
readings were although the absolute pressure was slightly off, the relative pressure and altitude adjustments from
point to point were spot on. Changes in pressure, as depicted from the graph above, transitioned nicely into
logical changes in altitude. At sea level, a change of roughly 1hPa corresponded to about an 8.43m change. The
results section will delve deeper into the successes of the pressure sensor in our test runs.
The gyro was a crucial aspect of our project because we needed it to compensate for the effects of gravity and
also gave us the rotation around all of the axes. As noted above in the hardware section, the triple axis gyro we used
was from adafruit. The chip that we chose supported both I2C and SPI communication so because we had already
implemented SPI for the pressure sensor and also to save pin connections, we chose to use the SPI interface.
Following along the standards of SPI discussed above, the gyroscope sensor shared clock and data input and
output lines with the pressure sensor. This is allowed because each chip had a unique CS or chip select line that
limited the devices to be activated mutually exclusively at different times. The gyroscope also came with a lot of
documentation, please refer to specific datasheets for each device in the reference section of this report. Our code
for communication with the gyroscope consists of two files: a header file,
gyro.h and a source file,
gyro.c. These and the functions are explained below.
Similar to the pressure sensor header file, in the gyro header file, we first initialized certain mode settings,
important registers, and also pin connections and variables. The mode settings were used to specify the sensitivity
of the device, essentially if we wanted to operate the device on a 250/500/2000 dps or degrees per second range.
We created a typedef enum
l3gd20Range_t to associate with the different sensitivity settings and
could be passed as an argument to the initialization function to configure the device. We then defined 15 registers
with their associated values that would be important in the destination addresses for reading and writing SPI functions
. We then defined the chip select pin to RPB5, physical pin 14 as dictated in the hardware section. Finally, we
defined four user functions that are described below:
int begin_gyro(l3gd20Range_t sensitivity); - Enabled all three, X,Y,Z gyro channels, set
the sensitivity of the device to either 250,500, or 2000 dps, and then made sure the gyro was responding correctly.
If the gyro was configured correctly, this function would return a 1, otherwise it would return a 0.
int debug_gyro(void); - Made sure the SPI channel was working and the gyroscope was
acknoledging. Called only in the
void writeGyroSpi(UINT8 address, UINT8 data); - Wrote the data to the gyroscope at
specific address using the standard SPI specification. The address was binary AND'D with 0x7F or 01111111
value so that the most significant bit was a 0, signifying a write operation.
float getGyro(INT16 address); - Retrieved the gyro data for a specific address. In this
case, the address was binary OR'd with the 0xC0 or 11000000 because the most significant 1 represented a read
operation and the second most significant bit incremented the address in multiple readings. The arguments would
OUT_X, OUT_Y, or OUT_Z signifying each axes angular velocity. Returned the
corresponding angular velocity in units of degrees per second as a float.
The final device that ran off of an SPI interface was the MicroSD breakout board. This board we got from
sparkfun. This particular board ran off of the SPI1 channel and the pin connections are described in the hardware
section above. In researching how to interface with the MicroSD and perform read and write operations on data files
stored within the FAT32 file structure, we found a project on
Mahbub's or Tahmid's Blog that read from a microSD. We were specifically interested in the libraries that he
used to interface with the SPI protocol. This was Microchip's open source file-system library MDDFS, a part of the
Microchip Libraries for Applications (MLA). The files that we used for our project are the same files that Tahmid used
for his. From the following folders, we included the following files:
FSDefs.h, FSIO.h, and SD-SPI.h
Compliler.h, FSconfig.h, and HardwareProfile.h
FSIO.c and SD-SPI.c
The following section describes the specific sections in the library that we modified to be congruent with our
project. In the
HardwareProfile.h header file, starting at line 529, we modified the define variables
to fit with the hardware that we mentioned above. The chip select, chip detect, data in, and data out pins were
configured to our hardware guidelines. The SPI clock frequency we used was 2MHz.
MDD_SDSPI_InitIO() function in the
SD-SPI.c source file, we set the
MOSI and MISO pins to be used for SDO1 and SDI1. All of the other files, we kept the way they were. The
Microchip library functions that we used are described below. These functions were used throughout the
main.c file and in the microSD protothreads function.
FSInit() - This function initializes the FAT32 file system on the microSD card. It
returns FALSE(0) if the initialization fails, and TRUE(1) otherwise. If a microSD card is not present in the
holder, the function returns FALSE. This function was called in the main function in a while loop to wait for a
card to be inserted.
FSFILE = FSfopen(filename, "a+") - This function opens a file to be operated on.
The filename is the name of the file that will be saved. The second argument specifies the intended use of the
open file, whether it is to be either read("r"), written("w"), or appended("a"). Additional, permissions are allowed
to allow both reading and writing or even appending and reading. The function will return NULL if opening the
file failed. Otherwise, the function returns a pointer of type FSFILE (defined in the library references) to be
used in the FSfwrite() function described next.
FSfwrite(buffer, sz, n, FSFILE pointer) - This function actually writes data to the
opened file. The data to be written is specified in a pointer of characters that can include data using the
sprintf() function in c. The two middle parameters define the number of bytes per input and the
total number of bytes to be written. The final parameter is the pointer that was returned from the
FSfclose(FSFILE pointer) -This function closes the file that was opened through
the FSFILE pointer argument.
Due to our technique in integrating the accelerometer data with the gyroscopic data to obtain velocity
we opted to do the numerical integration and velocity synthesis in post-processing. Our process is novel and
has exciting potential for further work, specifically in improving the accuracy of the velocity estimates.
As every physics student learns, acceleration is the first time derivative of velocity. Therefore, in simplest terms,
all that is needed to obtain a velocity is to integrate an acceleration function in time. However, there are two rather
issues with this approach. Firstly, as Einstein explained to us in his theories of relativity, it is impossible to distinguish
an acceleration from a gravitational force in a single reference frame. Secondly, we are not getting literally
continuous acceleration data. In the following work, we attempt solutions to both problems as many have before,
although perhaps in an innovative fashion.
The first issue demonstrates the necessity of the gyroscope, a tool measuring axial angular displacement. Our
solution here hinges on a use-case assumption, namely that the user must be stopped (with a velocity of 0) at the
beginning of data logging. This gives us a useful reference point of 0 m/s at the start, which is imperative for our
numerical integration to be meaningful. However, as we aren't moving at the start, we can assume that the only
measurement our accelerometer will be producing is that of earth's gravity. Therefore, from the first data point of
our dataset, we know the initial direction of gravity. From here, we can use the gyroscope to measure the change
orientation of our project to keep track of the direction of gravity in the future, allowing us to subtract it out from our
calculations, in theory yielding an acceleration that is going purely towards motion and therefore velocity.
The second issue gives way to the most drastic challenges face by the post-processing. As our acceleration data
is a set of samples from a continuous acceleration, our integration will not yield completely accurate results. In fact
it will yield an ever-accumulating error, which is one of the reasons that integrating acceleration data to get velocity
is so difficult and rarely pursued. We make a second assumption here to aid us in facing this challenge, namely
that the user will be completely stopped again when data logging is concluded. This now gives us a second reference
point from which to work in the analysis of the data, an invaluable tool and part of the novelty of our approach.
Numerical Integration of Accelerometer and Gyroscope Data
Gyroscope data needs first to be integrated in time in order to yield three total axial angular displacement arrays to
be used in gravity compensation. Later the three axial acceleration vectors will received the same treatment, so we
wrote a quick function, included here, that took as arguments the vector to be integrated and the corresponding time
array against which we integrate and returned the integral array.
We opted for trapezoidal integration because of the drastic increase in accuracy over the typical rectangular
technique taught in introductory calculus. However, for even greater accuracy, we suggest future work to utilize
Simpson's rule in integration. While often avoided for it's requirement of future values to perform its estimate, our
post-processing would have no trouble using it and may greatly benefit from it in the future.
def integrate( vect, time ):
'''This function returns the time integral of vect'''
integ = 
integral = 0
for i in range(1,len(vect)):
integral += 0.5*(vect[i] + vect[i-1])*(time[i] - time[i-1]) #trapezoidal integral
With angular displacement data handy, we can proceed to remove gravity's influence from our acceleration data.
To do this, we take the first line of data and, thanks to our first assumption above, take this to be gravity. Propagating
forward, we use an R3 three axis rotation matrix to calculate the new gravity axial components at each time step.
Improving this step in the process should be one of the primary focuses of further development, as incorrectly
compensating for gravity has the potential to produce very misleading results.
With gravity removed, we then integrated each acceleration component in time to produce three velocity arrays, which
are then combined using the 3-dimensional Pythagoren theorem at each time step to produce a single net velocity
magnitude array. Once correct, as described below, the velocity, temperature, pressure, altitude, and time data are
exported to a new CSV file for plotting in your choice of software. For ease we used Excel.
Error Mapping & Optimization of Velocity
With the ability to parse data and produce an initial velocity estimate, our script then moves to correct the ever-
present error. To try to estimate the type of error we could expect in our measurements, we held the data logger
as nearly perfectly still as possible for approximately a minute four different times, in different orientations, to give
use four calibration data sets. We knew that the velocity should be zero over the course of each entire data set, so
when we saw the velocity estimate produced we would be able to estimate the pervading error and compensate for it
to improve accuracy. Below are the four graphs depicting the uncorrected velocities calculated in each instance.
Clockwise from top left: Uncorrected calibration velocities 1, 2, 4, and 3.
right click and open images in new tab for better viewing.
Obviously these velocities get out of hand fast, with calibration set 2 appearing to surpass 1000 meters per second in
under two minutes. While we would have liked to explored the almost piecewise nature of the third calibration data set,
we assumed it to be an anomalous event and focused on the other three. Specifically we noticed that they were
seemingly very linear in their cumulative error. This may make sense if assume a constant acceleration error, which
when integrate would yield a linearly increasing error here. Each of their slopes, while similar in order of magnitude,
is unique. However, this issue was circumvented by our second usage assumption of no velocity at the stop of data
collection. This allows us to apply what we call a 'linear tilt' correction to the velocity. The correction can be pictured
as us tilting the velocity plot about a fixed origin until the final point is on the x-axis as we know it ought to be. By
applying this fix to calibration data sets 1, 2, and 4 we get the below corrected velocity plots.
Clockwise from top left: Linearly tilted calibration velocities 1, 2, and 4.
right click and open images in new tab for better viewing.
It should first be noted that this tilt has brought these velocities to a much more reasonable order of magnitude, with
only set 2 ever reaching the double digits. Now, while not satisfied with these new plots, we settled with this linear tilt
correction fully due to our limited time and resulting inability to further refine this error correcting process. We briefly
dabbled with quadratic error correcting under the assumption that there was some linearly increasing cumulative
error in the acceleration data resulting in a quadratic change in velocity, but to little avail. Code for this is included
but commented out in the downloadable Python script below.There are in fact interesting
commonalities between these corrected plots, strongly suggesting a need for further study into our error correction.
Of note are the sinusoidal behaviors as well as the 'belts' of tightness all between 20 and 30 seconds.
Lab Bench Test
Test done at the lab bench of the finished data logger are detailed in the software section above as the
calibration trials. The temperature, pressure, and altitude data from these trial runs is nearly perfectly
constant, which we expected, and is of little interest.
As of the writing of this report, it has yet to snow in Ithaca. For this reason, and for the aforementioned lack
of a proper housing, we opted to test our logger by driving down and uphill while opening and closing the
windows to test all of its sensors. We did an uphill and downhill run over the run depicted below at night.
Map of our test run
Please right click and open image in new tab for
This run is almost exclusively downhill with a stop sign at both the intersections with Stewart Ave and Willard Way.
Below are included the plotted velocity as well as temperature and altitude graphs of both the downhill and uphill
versions of this run.
Top: Downhill plots. Bottom: Uphill Plots
Plots Right: Altitude and Temperature Plots
Please right click and open image in new tab for
As noted in the software section detailing our post-processing techniques for velocity correction, these plots
aren't entirely meaningful. However, they are not devoid of interest, as it should be noted that the uphill plot
remains noticeably smaller in magnitude than the downhill plot, meaning our uphill driving speed was slower.
This was certainly the case, so while there's still work to be done to improve the velocity measurement's
accuracy, it still shows us something.
The altitude and temperature plots are of more interest however, as they
very accurately reflect what happened in our tests. As discussed above, the altitude was not absolutely correct
due to a lack of real-time knowledge of the sea-level air pressure in Ithaca during our run, but the relative
altitude change between start and end of both runs is extremely close to the true altitude change over the run.
Also notable is between seconds 40 and 50 in the downhill run when we opened the window of the heated car
to the outside cold, resulting in the decline in temperature seen there. On the uphill run we first began
reheating the car at full power, which accounts for the clear temperature rise, before again opening the
window to witness the temperature drop once again, albeit this time more steeply. The window was rolled up
just at the end of this uphill run, once again accounting for the slight upward trend over the last twenty
Safety was a major concern for us with our initial project proposal. Because we wanted to have a heads-
up-display, we needed a small enough screen set in the peripheral vision of the user so that it would not be
distracting or get in the way during live activities. After a lot of thought and consideration, we decided that this
would not be a good idea. We were not able to find a screen smaller than an inch which would still take up a lot
of space when put in front of someone's eye and inside a ski goggle or helmet. Therefore, we decided to ditch
the idea of a screen that provided real-time updates altogether. After this adjustment to our project, there are no
other safety concerns other than possible electrical contact with skin.
We tried to design this project with usability in mind. Obviously, there are improvements that could be made
in this regard, but overall the device was fairly easy to use. Once power was supplied to the microcontroller,
one just needed to insert the microSD card and it would begin logging data. It would have been nice if we
put the hardware and sensors on a solder board, enclosed the system with an external battery power supply and
switch to turn the system on and off so that it could be shielded from the elements. Another feature worth
implementing would be a mechanism by which you could stop one data logging to later start another, saving
multiple runs in uniquely named CSV's.
Our design worked nearly as anticipated. As discussed, the velocity synthesis has work yet to be done on it,
but aside from that our data logger runs perfectly. Temperature and pressure data are taken perfectly, yielding
a very accurate altitude measurement as well. Our SPI communication with the SD Card reader, used to create
our data logs in the CSV file format, was completely successful as well. The biggest issues we ran into over the
course of this project involved getting the nuances of each SPI protocol for each different device using it to
work and the designing of a velocity synthesis algorithm. We spent a great deal of time reading data sheets
to find the names of specific registers for various sensors as well as troubleshooting SPI signals using an
oscilloscope. Other troubles along the way included breadboards which had power rails discontinuous in the
middle unbeknownst to us as well as broken ground wires.
With further development, we believe that there is a possibility for our synthesis of velocity given both
acceleration and gyroscope data to be patented. Based on our research online, it is a rather novel approach
to the integration of the two data given our usage assumptions. We would be interested to hear from anyone
should they pursue this further themselves, and may update this project in the future with our own work.
All software tools we used, such as the MPLab IDE, Python, and various software libraries, are freely available
to the public. As such we do not believe that any of our work violates any intellectual property rights or laws. We
also believe our final logger to be dissimilar enough from anything existing on the market today so as not to be
Having switched from a heads up display to a data logging tool, as well as the usage assumption (which would
be included as an instruction in any commercial release) of being still at start and stop of data logging, we feel
comfortable that our project cannot make skiing any more dangerous than it already is. Without endangering
anybody, we don't feel our project to be in any way unethical.
A. Program Listing
The below first link contains the project code that was run on the microcontroller. The second link is to the
Python script used to parse the CSV files from the MicroSD Card.
PIC project download here
Python data parser download here
Project Schematic: This schematic more thoroughly details the pins
we used for each of our connections.
Please right click and open image in new tab for
C. Parts List
|BMP183 Pressure Sensor
|MicroSD Transflash Breakout
|8GB SD Card
D. Division of Labor
Almost all of the project, from design conception, to writing the code, and implementing the hardware design,
was all done together. However, there was more focus for Matt on writing the PIC code and implementing the
SPI communication protocols. Aidan focused his work on the python post-processing script. Both parties spend a
great deal of time debugging as the project progressed, both separately and together.
This section provides links to external reference documents, code, and websites
used throughout the project.
We would like to thank Bruce Land for a great class and for all the help and guidance offered throughout this
project. We would also like to thank our lab TAs, specifically Shiva, for helping us debug our work and keeping the
lab open for so many hours.