Model Rocket Data Logger
Logging of altitude and acceleration telemetry for model rockets
About this Code Example
This project is intended to characterize the flight of a model rocket, in terms of acceleration and altitude. Taking advantage of the new Curiosity Nano platform to help with development, development went from initial configuration and measurement setup with Mikroe Clicks on a Curiosity Nano Adapter to a final implementation on a custom board with the same code base throughout.
- Load the board into the rocket
- Turn the rocket upside down, press the button, wait for the LED to turn off
- Turn the rocket right-side up, press the button, wait for the LED to turn off
- Get the rocket ready to launch
- Arm the trigger by pressing the button. The LED should turn on and stay on
- Launch the rocket
- Recover the rocket. The LED should be blinking. Press the button and it should stop
- Connect the board to the computer
- Run the script to recover and view the data
The hardware must accomplish the following:
- Detect the launch
- Capture acceleration and altitude
- Convert/communicate the results to the microcontroller
- Store the data
- Communicate the logged data to a PC
To accomplish this, we need:
- Acceleration and altitude sensors
- ADCC and MSSP peripherals to convert/communicate the data from the sensors
- A serial SRAM to store the data
- A PIC16F18446 Nano with programmer and USB-Serial adapter on board
The accelerometers are a BMI088 3 axis +/-25 g I2C sensor and an ADXL377 3 axis +/-200 g analog sensor. Together they provide a high maximum range for launches while still allowing for good resolution at low g post launch. The ADCC peripheral of the microcontroller is a 10-bit ADC with a computational unit which can run continuous conversions with an interrupt when the input exceeds a programmed threshold. The altitude sensor is a BME280 temperature, humidity, and barometric pressure sensor with an I2C interface. The MSSP peripheral of the microcontroller provides the necessary I2C master to talk to both the BME280 and BMI088.
The data storage is supplied by a 23K640 serial SRAM on a SPI interface, connected to the second MSSP peripheral on the microcontroller. The SRAM has 64 Kb of memory organized as an 8K x 8-bit memory. Data is transmitted to the PC via the EUSART peripheral which is connected to the programmer/serial port logic built into the PIC16F18446 Nano.
Power is supplied by a CR2032 coin cell which provides 3.2 V and 230 mAh of storage. When not in use (no pending launch), the microcontroller puts all external, and many internal, peripheral functions to sleep to minimize battery current. Under normal storage operations, the battery should last between 200 and 300 hours or 8-12 days.
Click Bench Test
Initial configuration and hardware abstraction layer programming was completed on a Curiosity Nano Adapter with Click boards from Mikroe for the various sensors/memory. This allowed for software development prior to board design/assembly being completed. The following clicks were used:
- Weather Click - BME280
- PROTO Click - With the following attachments
- 3x potentiometer - analog accelerometer simulation
- Grove 6-Axis Board - BMI088
- 23K640 SRAM
The following peripherals were used, configured in MCC:
- DAC: Used to measure battery voltage
- MSSP2: Used for I2C for the digital accelerometer and BME280.
- MSSP1: Used for SPI for 23k640 SRAM
- EUSART: Used to transfer data to the PC
- ADCC: Used to read the analog accelerometer, as well as to measure battery voltage
- TMR1: Used to periodically wake the device to log data
- FVR: Used to measure battery voltage against
For the timer specifically, setup is as follows
LFINTOSC is used as the source to minimize power use while asleep between data logging. The timer period is adjusted in software depending on how long is required between logs.
- SLEEP: The device is in minimum power consumption with the sensors and microcontroller in sleep mode. When awoken by a UART transmission, it will awake, transmit the contents of the SRAM, and return to sleep. When awoken by an Interrupt on Change from the button, it will go to STARTUP.
- STARTUP: The sensors are awoken from deep sleep, given time to wake up, and then the state changes to CALIBRATION_DOWN.
- CALIBRATION_DOWN: The downward facing acceleration values are collected. The device is then put to sleep until woken by a button press, at which point it goes to CALIBRATION_UP.
- CALIBRATION_UP: The upward facing acceleration values, as well as the calibration values, initial pressure, and temperature are collected. The device is then put into SETUP mode.
- SETUP: This is the state in which the rocket is meant to be put onto the launch platform. Once setup is complete, the button can be pressed. This arms the continuous accelerometer measurement threshold which is triggered by launch, and the state is changed to STANDBY.
- STANDBY: This is the state where the rocket waits to launch. If the button is pressed again, the device returns to the CALIBRATION_DOWN state, in case there was a mistake during calibration. Otherwise, if there is a spike in acceleration, measurement on the analog accelerometer and pressure sensor are enabled at 10 Hz and the state is changed to LAUNCH.
- LAUNCH: Every time the device is woken by the timer, a reading is logged. Additionally, it is determined if the acceleration is still above a minimum threshold, indicating continued powered flight. If it is not, the analog accelerometer is disabled, the digital accelerometer is enabled, the sample rate is changed to 2 Hz, and the state is changed to flight.
- FLIGHT: Every time the device is woken by the timer, a reading is logged. If the memory is full, the state changes to END. If the device is woken by a button press, the state changes to END.
- END: All logging is stopped, the sensors are put into sleep mode, and the state is changed to SLEEP.
Encoding and Transmission
The storage of the data makes use of a modified form of Consistent Overhead Byte Stuffing (COBS), where there are packets of COBS encoded data interspersed with unencoded metadata. It was also modified to replace 0x00 with 0x0A ('\n') and vice versa to make parsing on the PC side easier.
The initial segment of storage contains a fixed size and format calibration block. This contains the +/-1 g readings from both the digital and analog accelerometer, the temperature for use in calculating altitude, the initial pressure, and the calibration data for the pressure and temperature readings.
Before each segment of data, there is a three byte metadata packet to indicate the format and timing of the data following it. The first byte is an unencoded 0x00 which indicates the end of the preceding segment. Next is a byte which indicates which sensors are being read in the following segment. The final byte indicates the timing between measurements.
Following this metadata packet is a stream of the raw readings from the enabled sensors. Because the metadata packet indicates which sensors are being read, the processing script on the PC knows how many bytes each reading is, which is expanded on in the Post-Processing section.
At the end of the data is an unencoded 0x00 indicating the end of the segment followed by 0xFF indicating the end of the data.
Transmission is initiated when the device is in the SLEEP state and receives a break signal to the EUSART. Once the break has concluded, the data is transmitted as stored in the SRAM, and the device returns to sleep.
Post-processing code is in the zip file in the project called Visualizer. It is written for Python 3.5 and makes used of the matplotlib and pyserial packages, as well as their respective dependencies.
As stated above, the implementation of COBS was modified to swap 0x00 and '\n' in the data. Because there is a '\n' at the start of each new segment of metadata/data, the Python function serial.readlines() will conveniently return the data broken up into segments of the same data format, making processing easier. On read, any instances of 0x00 are replaced with 0x0A to return the data to its correct value.
Firstly, the calibration values are read. For the accelerometers, this will determine what reading corresponds with 0 g (halfway between upside down and right-side up) as well as scaling values to be in terms of g (1 g calculated as half the difference between upside down and right-side up). The calibration data from the BME280 is read for use in the equations to calculate temperature and pressure from the raw readings. The raw temperature reading is read and converted to Celsius for use in determining altitude from pressure. Finally, the initial pressure value is read to determine initial altitude, since all altitude values will be presented as a delta of the initial altitude.
The equation for determining altitude given pressure and temperature is the following:
where P0 is sea level pressure, P is the measured pressure, and T is the temperature in degrees Celsius. The initial altitude is subtracted from all readings after conversion, giving an altitude delta, rather than height above sea level.
Once the calibration is read, the data is processed section by section. At the start of each section is the metadata, which indicates what data is in each capture (and therefore how many bytes each capture is and how the data is arranged), as well as the time between each capture. For instance, if the digital accelerometer and pressure sensor are enabled, each capture will be 9 bytes in the format XxYyZzPpp (where x, y, and z are axes of the accelerometer and p is the pressure reading) and the data is processed one 9 byte chunk at a time.
The first 6 bytes are the accelerometer, so those 6 bytes are then split into two byte pairs for each axis, and then converted from raw data to values in terms of g. The magnitude of the vector sum is then calculated to determine the total acceleration being experienced.
Next the final 3 bytes are converted to pressure and then used to calculate altitude. All three values are stored in a 2D array consisting of time (the previous time + the time between samples), acceleration (either from the analog or digital accelerometer), and the altitude (if enabled).
Once there is a time ordered array of acceleration and altitude, some cleanup on the accelerometer is necessary to adjust the value from force measured on the sensor to actual acceleration of the rocket (for instance, while sitting still, the accelerometer reads -1 g, and when in free fall reads 0 g). During the first section of the flight, the value is inverted (to compensate for the fact that acceleration is being measured opposite the direction it's actually occurring) and then 1 g is subtracted from it (to compensate for the fact that gravity is applying equally to both the rocket and the sensor, and is therefore not measured).
Once the rocket reaches its peak (determined by the maximum value of the altitude), the rocket flips so that the nose is now pointing down, so the value is no longer inverted. 1 g is still subtracted to account for the force of gravity applying equally to the sensor and rocket.
The two sets of values are then graphed versus the time values to produce a graph of acceleration and altitude versus time.
- Analog Interface
- MPLAB Code Configurator (MCC)
- Core Independent
- Asynchronous / Synchronous Communication
- Feb 25, 2019