Rocket with this flight computer onboard for data collection!
I designed this flight computer using KiCad as a hands-on way to explore PCB prototyping and design. It features an STM32F103C8T6 microcontroller for computation, paired with an MS5607-02BA03 Barometric Pressure Sensor for data collection and altitude detection. The barometric sensor is configured at its maximum oversampling rate, achieving an internal ADC conversion time of 0.6 milliseconds for each measurement of temperature and pressure.
A key feature of this design is its ability to rapidly log data to an SD card, allowing for reliable real-time storage of high-frequency measurements. This capability ensures all flight data is preserved for post-flight analysis. The SD logging system is optimized to minimize processor utilization and can be safely suspended and resumed by higher-priority processes.
The flight computer is built with reverse polarity and overcurrent protection, ensuring safety and reliability of the hardware. Additionally, it includes six peripheral pins for I2C and SPI communication, enabling connectivity with external devices such as sensors, telemetry modules, or any other components requiring GPIO pins. This expandability makes the flight computer highly versatile for a range of applications.
Data collection is managed inside a high-priority timer interrupt state machine, ensuring precise timing between samples. During execution, the main loop is suspended to maintain uninterrupted operation. The flight computer utilizes a double-buffering setup, where data is collected into a compact array format. When the buffer is full, the system switches to the alternate buffer, allowing the full buffer to be extracted and written to the SD card without interrupting data collection. This approach supports extremely high sampling rates as many samples can be temporarily stored in RAM and then written to the SD card in a single write operation.
The deployment algorithm is executed within an interrupt loop, periodically sampling pressure data to calculate velocity. This velocity measurement is used to detect critical events such as launch and apogee, enabling precise deployment actions.
For deployment, the flight computer uses four AO3400A N-Channel MOSFETs, capable of providing up to 2A of current to an E-Match or nichrome wire.
Below is an example code snippet illustrating how the data collection hardware interrupt loop operates:
// Implement the callback function
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
// Handle Timer 2 interrupt
HAL_GPIO_WritePin(WHITE_LED_GPIO_Port, WHITE_LED_Pin, GPIO_PIN_SET);
switch (stateMachine)
{
case STARTUP: // we only go to state STARTUP once when we start the FC to send the first write command
MS5607_WriteCommand(&barometer, MS5607_CMD_CONVERT_D1); // get pressure in 100ths of a millibar
stateMachine = READ_PRESSURE;
__HAL_TIM_SET_COUNTER(&htim1, 0);
return;
case READ_PRESSURE:
D1 = MS5607_ReadADC(&barometer);
MS5607_WriteCommand(&barometer, MS5607_CMD_CONVERT_D2); // get temperature in 100ths of degree C
stateMachine = READ_TEMPERATURE_AND_CALC;
return;
case READ_TEMPERATURE_AND_CALC:
D2 = MS5607_ReadADC(&barometer);
loopTime = __HAL_TIM_GET_COUNTER(&htim1);
int32_t dT = D2 - ((uint32_t)C[5] << 8);
int32_t TEMP = 2000 + ((int64_t)dT * C[6] >> 23);
int64_t OFF = ((int64_t)C[2] << 17) + ((int64_t)C[4] * dT >> 6);
int64_t SENS = ((int64_t)C[1] << 16) + ((int64_t)C[3] * dT >> 7);
int32_t P = (((D1 * SENS) >> 21) - OFF) >> 15;
// logCounter = 24 bits
// loopTime = 16 bits
// modifiedTEMP = 16 bits
// modifiedP = 24 bits
// Check if buffer is full
if (bufferIndex < BUFFER_SIZE)
{
// Store data in the current buffer, this bitpacking scheme assumes the numbers never overflow, which may not be true
currentBuffer[bufferIndex++] = (logCounter >> 16) & 0xFF;
currentBuffer[bufferIndex++] = (logCounter >> 8) & 0xFF;
currentBuffer[bufferIndex++] = logCounter & 0xFF;
logCounter++;
currentBuffer[bufferIndex++] = (loopTime >> 8) & 0xFF;
currentBuffer[bufferIndex++] = loopTime & 0xFF;
currentBuffer[bufferIndex++] = (TEMP >> 8) & 0xFF;
currentBuffer[bufferIndex++] = TEMP & 0xFF;
currentBuffer[bufferIndex++] = (P >> 16) & 0xFF;
currentBuffer[bufferIndex++] = (P >> 8) & 0xFF;
currentBuffer[bufferIndex++] = P & 0xFF;
}
else
{
// Buffer full, switch buffers and reset index
if (HAL_GPIO_ReadPin(GREEN_LED_GPIO_Port, GREEN_LED_Pin))
{
HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_RESET); // GREEN_LED low
}
else
{
HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_SET); // GREEN_LED high
}
bufferFull = 1;
writeBuffer = currentBuffer;
// Switch to the alternate buffer
if (currentBuffer == firstBuffer)
{
currentBuffer = secondBuffer;
}
else
{
currentBuffer = firstBuffer;
}
bufferIndex = 0;
}
MS5607_WriteCommand(&barometer, MS5607_CMD_CONVERT_D1); // get pressure in 100ths of a millibar
stateMachine = READ_PRESSURE;
__HAL_TIM_SET_COUNTER(&htim1, 0);
return;
default:
Error_Handler();
}
}
}