diff --git a/README.md b/README.md index 3556b0b..2b2b2af 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Catalyst 2 Flight Computer -Designed for Teensy 4.1 +# Stimulant +Hardware agnostic flight software for high power rockets. -Want to contribute? Start [here](https://github.com/Harlem-Launch-Alliance/Catalyst-2/blob/main/GETTING_STARTED.md). +Want to contribute? Start [here](https://github.com/Harlem-Launch-Alliance/Stimulant/blob/main/docs/GETTING_STARTED.md). # NOTE: At this time we are only accepting contributions from [HLA](hla.nyc) members for issues with the `beginner` label. -[Instructions for use](https://github.com/Harlem-Launch-Alliance/Catalyst-2#instructions-for-use) +[Instructions for use](https://github.com/Harlem-Launch-Alliance/Stimulant#instructions-for-use) ## Goals: | Feature | Status | @@ -26,29 +26,32 @@ Want to contribute? Start [here](https://github.com/Harlem-Launch-Alliance/Catal [Flow Chart](https://miro.com/app/board/uXjVPYUZ7mY=/?share_link_id=493963314311) ## Hardware: +This software can support any configuration of hardware with exactly one of each of the following types: +- Microcontroller +- Inertial Measurement Unit +- GPS +- Radio transceiver + +The following hardware is currently supported: - Teensy 4.1 -- Adafruit BMP388 Barometric Altimeter +- RaspberryPi Pico +- Adafruit BMP388/BMP390 Barometric Altimeter - GY521 IMU (MPU 6050) +- LSM6DSOX IMU - Adafruit Ultimate GPS - Xbee-PRO S3B -- 5V buzzer - -## Dependencies: -- https://github.com/adafruit/Adafruit_BMP3XX -- https://github.com/adafruit/Adafruit_GPS ## Best Practices For Making Changes: - make sure your code compiles to verify your changes - comment your code -- if you are including a new library in your code, add the GitHub link to the dependencies list above - multiple small functions are better than one big one. Remember, each function should only do one thing - break out groups of functions into their own files. Long files are difficult to read. Ideally `main.ino` would only have `void setup()` and `void loop()` ## Instructions for use: 1. Connect all sensors and power source -2. Integrate Teensy with rocket (try to keep IMU aligned with axis of rotation) +2. Integrate flight computer with rocket (try to keep IMU aligned with axis of rotation) 3. Put rocket on launch rail -4. Power on Teensy +4. Power on flight computer * **CAUTION**: Once powered on, any sudden movements may trigger launch detection 5. Wait at least 5 minutes between powering on and launching to allow for proper sensor calibration 6. Launch! diff --git a/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to docs/CODE_OF_CONDUCT.md diff --git a/GETTING_STARTED.md b/docs/GETTING_STARTED.md similarity index 100% rename from GETTING_STARTED.md rename to docs/GETTING_STARTED.md diff --git a/pull_request_template.md b/docs/pull_request_template.md similarity index 100% rename from pull_request_template.md rename to docs/pull_request_template.md diff --git a/main/apogee.h b/main/apogee.h index c89d3f0..2331e7f 100644 --- a/main/apogee.h +++ b/main/apogee.h @@ -2,13 +2,10 @@ * apogee detection and helper functions * ****************************************************************************/ +#pragma once -#include "Ewma.h" -#include "utils.h" - -#define G_FORCE_TO_LAUNCH 3 //if acceleration exceeds this number the rocket will assume it has been launched -#define MAX_APOGEE_ACCEL 2 //we can rule out apogee if acceleration is about this amount (Gs) -//#define CHECK_FOR_APOGEE_HZ 4 //frequency that we check for apogee within the detect apogee function +#include "utils/datatypes.h" +#include "utils/Ewma.h" /** * @brief Detect if rocket has been launched using acceleration data diff --git a/main/attitude.h b/main/attitude.h index 5bfcd67..87f2b47 100644 --- a/main/attitude.h +++ b/main/attitude.h @@ -7,8 +7,10 @@ //rates are magnitudes of rad/s //assuming roll == 0, pitchRate will be equivalent to y, and yawRate will be equivalent to x -#include "Ewma.h" -#include "utils.h" +#pragma once + +#include "utils/datatypes.h" +#include "utils/Ewma.h" /// @brief this object tracks attitude/orientation as well as gyroscope calibration values class Attitude { diff --git a/main/config/birdbrain.h b/main/config/birdbrain.h new file mode 100644 index 0000000..9615cd7 --- /dev/null +++ b/main/config/birdbrain.h @@ -0,0 +1,35 @@ +/***************************************************************************** + * This file is for any settings or constants that may be manually adjusted + * specific to the Birdbrain flight computer + ****************************************************************************/ +#pragma once + +//tick times (microseconds) +constexpr uint32_t TICK_TIME_ONPAD = 10000; +constexpr uint32_t TICK_TIME_ASCENDING = 10000; +constexpr uint32_t TICK_TIME_DESCENDING = 50000; +constexpr uint32_t TICK_TIME_POST_FLIGHT = 1000000; + +constexpr uint32_t GPS_WAIT_TIME = 900; //number of seconds to wait for GPS to acquire signal before moving on + +constexpr uint32_t RING_QUEUE_LENGTH = 1000;//each 100 elements is 1 seconds of data at 100hz + +// pin definitions +constexpr int BMP_CS = 7; //SPI chipSelect of bmp altimeter +constexpr int BUILTIN_SDCARD = 9; // SPI chipSelect of SD card +constexpr int RADIO_CS = 8; //SPI chipSelect of radio module +constexpr int IMU_CS = 6; //SPI chipSelect of IMU +constexpr int SPI0_SCK = 2; //SPI clock pin +constexpr int SPI0_MOSI = 3; //SPI MOSI pin +constexpr int SPI0_MISO = 4; //SPI MISO pin +constexpr int SPI1_SCK = 10; +constexpr int SPI1_MOSI = 11; +constexpr int SPI1_MISO = 12; +constexpr int BUZZER_PIN = 26; +constexpr int PYRO0_PIN = 14; +constexpr int PYRO1_PIN = 15; + +constexpr int TONE_HZ = 2500; //frequency of buzzer tone + +#define GPSSerial Serial1 //Hardware Serial Location of GPS +#define XBeeSerial Serial //Hardware Serial Location of XBee diff --git a/main/settings.h b/main/config/catalyst2.h similarity index 63% rename from main/settings.h rename to main/config/catalyst2.h index c608ee4..202e536 100644 --- a/main/settings.h +++ b/main/config/catalyst2.h @@ -1,15 +1,9 @@ /***************************************************************************** * This file is for any settings or constants that may be manually adjusted - * + * specific to the Catalyst-2 flight computer ****************************************************************************/ #pragma once -#ifndef UNIT_TEST -#include -#else -#include "../test/lib/mock_arduino.h" -#endif // UNIT_TEST - //tick times (microseconds) constexpr uint32_t TICK_TIME_ONPAD = 10000; constexpr uint32_t TICK_TIME_ASCENDING = 10000; @@ -18,14 +12,21 @@ constexpr uint32_t TICK_TIME_POST_FLIGHT = 1000000; constexpr uint32_t GPS_WAIT_TIME = 300; //number of seconds to wait for GPS to acquire signal before moving on +constexpr uint32_t RING_QUEUE_LENGTH = 3000;//each 100 elements is 1 second of data at 100hz + +// pin definitions constexpr int BMP_CS = 10; //SPI chipSelect of bmp altimeter -#ifdef PICO -#define GPSSerial Serial1 //Hardware Serial Location of GPS -#define XBeeSerial Serial //Hardware Serial Location of XBee -constexpr uint32_t RING_QUEUE_LENGTH = 1000;//each 100 elements is 1 seconds of data at 100hz -#else +constexpr int SPI0_SCK = 11; //SPI clock pin (used for built in SD card) +constexpr int SPI0_MOSI = 12; //SPI MOSI pin +constexpr int SPI0_MISO = 13; //SPI MISO pin +constexpr int SPI1_SCK = SPI0_SCK; +constexpr int SPI1_MOSI = SPI0_MOSI; +constexpr int SPI1_MISO = SPI0_MISO; +constexpr int BUZZER_PIN = 33; +constexpr int PYRO0_PIN = 34; +constexpr int PYRO1_PIN = 35; + +constexpr int TONE_HZ = 1000; //frequency of buzzer tone + #define XBeeSerial Serial1 //Hardware Serial Location of XBee #define GPSSerial Serial2 //Hardware Serial Location of GPS -constexpr uint32_t RING_QUEUE_LENGTH = 3000;//each 100 elements is 1 seconds of data at 100hz -#endif // PICO -constexpr int BUZZER_PIN = 33; diff --git a/main/config/config.h b/main/config/config.h new file mode 100644 index 0000000..0b6eb94 --- /dev/null +++ b/main/config/config.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * This file is for any settings or constants that may be manually adjusted + * not specific to any hardware architecture + * all hardware specific settings should be in their own file + * + * all hardware architectures should be conditionally included here + ****************************************************************************/ +#pragma once + +#ifdef UNIT_TEST +#include "../test/lib/mock_arduino.h" +#else +#include +#endif // UNIT_TEST + +#if defined(BIRDBRAIN) +#include "config/birdbrain.h" +#elif defined(CATALYST2) +#include "config/catalyst2.h" +#endif + +constexpr double MAIN_ALTITUDE = 200; //altitude to deploy main parachute at (meters) +constexpr double G_FORCE_TO_LAUNCH = 2; //if acceleration exceeds this number the rocket will assume it has been launched +constexpr double MAX_APOGEE_ACCEL = 1; //we can rule out apogee if acceleration is above this amount (Gs) diff --git a/main/data.h b/main/data.h index 20bc7f7..45b6065 100644 --- a/main/data.h +++ b/main/data.h @@ -2,16 +2,15 @@ * This file is for saving and transmitting data * currently using an SD card and Xbee respectively ****************************************************************************/ -#include "ringQueue.h" -#include "settings.h" -#include "utils.h" +#pragma once + +#include "config/config.h" +#include "utils/datatypes.h" +#include "utils/ringQueue.h" + #include -#ifndef PICO const int CS_SD = BUILTIN_SDCARD; -#else -const int CS_SD = 15; -#endif // PICO char filename[50]; RingQueue imuQueue; @@ -56,7 +55,7 @@ void setupSD(String date){ } else XBeeSerial.println("MicroSD card: successful"); - String filestart = "Catalyst2-"; + String filestart = "Stimulant-"; filestart.concat(date); filestart.concat(".csv"); filestart.toCharArray(filename, 50); @@ -67,6 +66,9 @@ void setupSD(String date){ } else { XBeeSerial.println("MicroSD card: error opening file"); + XBeeSerial.print("Filename: "); + XBeeSerial.println(filename); + delay(5000); } } @@ -112,7 +114,9 @@ void recordData(gpsReading sample, bool prelaunch){ * */ void backupToSD(){ + XBeeSerial.println("backing up..."); File dataFile = SD.open(filename, FILE_WRITE); + char buffer[50]; if(!dataFile){ XBeeSerial.println("error opening datalog"); return; @@ -140,27 +144,45 @@ void backupToSD(){ gpsQueue.dequeue(); } } - dataFile.print(imuSample.time); dataFile.print(','); - if(altitude != 0) - dataFile.print(altitude, 1); + itoa(imuSample.time,buffer,10); + dataFile.print(buffer); dataFile.print(','); + if(altitude != 0) { + dtostrf(altitude, 6, 1, buffer); + dataFile.print(buffer); + } dataFile.print(','); - if(latitude != 0) - dataFile.print(latitude, 4); + if(latitude != 0) { + dtostrf(latitude, 6, 4, buffer); + dataFile.print(buffer); + } dataFile.print(','); - if(longitude != 0) - dataFile.print(longitude, 4); + if(longitude != 0) { + dtostrf(longitude, 6, 4, buffer); + dataFile.print(buffer); + } dataFile.print(','); - dataFile.print(imuSample.gyro.x); dataFile.print(','); - dataFile.print(imuSample.gyro.y); dataFile.print(','); - dataFile.print(imuSample.gyro.z); dataFile.print(','); - dataFile.print(imuSample.accel.x); dataFile.print(','); - dataFile.print(imuSample.accel.y); dataFile.print(','); - dataFile.print(imuSample.accel.z); dataFile.print(','); - dataFile.print(imuSample.attitude.x); dataFile.print(','); - dataFile.print(imuSample.attitude.y); dataFile.print(','); - dataFile.print(imuSample.attitude.z); dataFile.print(','); - if(altitude != 0) - dataFile.print(state); + dtostrf(imuSample.gyro.x, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.gyro.y, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.gyro.z, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.accel.x, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.accel.y, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.accel.z, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.attitude.x, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.attitude.y, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + dtostrf(imuSample.attitude.z, 8, 4, buffer); + dataFile.print(buffer); dataFile.print(','); + if(altitude != 0) { + itoa(state,buffer,10); + dataFile.print(buffer); + } dataFile.println(','); } while(!bmpQueue.isEmpty()){ @@ -176,13 +198,20 @@ void backupToSD(){ gpsQueue.dequeue(); } } - dataFile.print(bmpSample.time); dataFile.print(','); - dataFile.print(bmpSample.altitude, 1); dataFile.print(','); - if(latitude != 0) - dataFile.print(latitude, 4); + itoa(bmpSample.time,buffer,10); + dataFile.print(buffer); dataFile.print(','); + dtostrf(bmpSample.altitude, 6, 1, buffer); + dataFile.print(buffer); dataFile.print(','); + if(latitude != 0) { + dtostrf(latitude, 6, 4, buffer); + dataFile.print(buffer); + } + dataFile.print(','); + if(longitude != 0) { + dtostrf(longitude, 6, 4, buffer); + dataFile.print(buffer); + } dataFile.print(','); - if(longitude != 0) - dataFile.print(longitude, 4); dataFile.print(','); dataFile.print(','); dataFile.print(','); @@ -193,8 +222,10 @@ void backupToSD(){ dataFile.print(','); dataFile.print(','); dataFile.print(','); - dataFile.print(bmpSample.state); + itoa(bmpSample.state,buffer,10); + dataFile.print(buffer); dataFile.println(','); } - dataFile.close(); + dataFile.close(); + XBeeSerial.println("back up complete"); } diff --git a/main/main.ino b/main/main.ino index 23b7f12..22bb969 100644 --- a/main/main.ino +++ b/main/main.ino @@ -1,38 +1,55 @@ /************************************************************************************* Harlem Launch Alliance - Recovery Systems Group 2021 - 2022 - Catalyst 2.1 Flight Computer + Avionics Group 2021 - 2024 + Stimulant Flight Software *************************************************************************************/ #include "apogee.h" -#include "bmp_altimeter.h" -#include "data.h" -#include "gy521_imu.h" #include "attitude.h" -#include "gps.h" +#include "data.h" +#include "sensors/altimeter/bmp3xx.h" +#include "sensors/gps/adafruit_gps.h" +#include "sensors/imu/imu.h" +#include "utils/utils.h" +imu imu; Attitude attitude; void setup() { + delay(500); // give sensors time to boot up + + // set up radio XBeeSerial.begin(115200); // xBee baudrate: 115200, 9600 XBeeSerial.print("\n\n\n"); pinMode(BUZZER_PIN, OUTPUT); // Set buzzer pin as an output + + //TODO: replace with pyro setup function + pinMode(PYRO0_PIN, OUTPUT); // Set pyro pins as an outputs + pinMode(PYRO1_PIN, OUTPUT); + digitalWrite(PYRO0_PIN, LOW); // Set pyro pins to low + digitalWrite(PYRO1_PIN, LOW); + for(int i = 0; i < 5; i++) // Play 5 beeps { - tone(BUZZER_PIN, 1000); // Send 1KHz sound signal... + //tone(BUZZER_PIN, TONE_HZ); // Play a tone... XBeeSerial.println("beep!"); delay(1000); // ...for 1 sec - noTone(BUZZER_PIN); // Stop sound... + //noTone(BUZZER_PIN); // Stop sound... delay(1000); // ...for 1sec } - Wire.begin(); // initiate wire library and I2C setupBMP(); - setupIMU(); + delay(2000); + + imu.setup(); + delay(2000); String date = setupGPS(); + delay(2000); + setupSD(date); + delay(5000); } flightPhase runOnPad(uint32_t tick); @@ -84,7 +101,7 @@ flightPhase runOnPad(uint32_t tick){ static bmpReading lastBmp; static gpsReading lastGps; - imuReading imuSample = getIMU(); //sample IMU first to maximize consistency + imuReading imuSample = imu.sample(); //sample IMU first to maximize consistency bool hasLaunched = detectLaunch(imuSample.accel); //might need to calibrate accel data before feeding to this function if(tick % 5 == 0){ @@ -101,6 +118,7 @@ flightPhase runOnPad(uint32_t tick){ recordData(imuSample, true); if(tick % 100 == 2 && lastGps.longitude == 0){//1 reading then use cached value + //TODO: if there's no GPS this could be a problem lastGps = getGPS(); recordData(lastGps, true); } @@ -126,7 +144,7 @@ flightPhase runAscending(uint32_t tick){ //this will run similarly to ONPAD exce static bmpReading lastBmp; static gpsReading lastGps; bool apogeeReached = false; - imuReading imuSample = getIMU(); //sample IMU first to maximize consistency + imuReading imuSample = imu.sample(); //sample IMU first to maximize consistency if(tick % 5 == 0){ lastBmp = getBMP(); @@ -150,6 +168,7 @@ flightPhase runDescending(uint32_t tick){//this runs at 20hz static gpsReading lastGps; static double lastAlt = getBMP().altitude + 5;//initialize so that landing detection isn't triggered accidentally static bool initialDump = false; + static bool deployedMain = false; if(!initialDump) { initialDump = true; @@ -161,6 +180,22 @@ flightPhase runDescending(uint32_t tick){//this runs at 20hz bmpSample.state = 2; //this corresponds to DESCENDING recordData(bmpSample, false); + //TODO: or vertical velocity above threshold (50m/s?) + static double altForVelocity = bmpSample.altitude; + double currentVelocity = (altForVelocity - bmpSample.altitude) * 20; //delta alt divided by dt (.05) + + if ((bmpSample.altitude < MAIN_ALTITUDE || currentVelocity > 50) && !deployedMain) { + digitalWrite(PYRO0_PIN, HIGH); + delay(1000); + digitalWrite(PYRO0_PIN, LOW); + digitalWrite(PYRO1_PIN, HIGH); + delay(1000); + digitalWrite(PYRO1_PIN, LOW); + deployedMain = true; + } + + altForVelocity = bmpSample.altitude; + if(tick % 20 == 1){//still one time per second lastGps = getGPS(); recordData(lastGps, false); diff --git a/main/bmp_altimeter.h b/main/sensors/altimeter/bmp3xx.h similarity index 95% rename from main/bmp_altimeter.h rename to main/sensors/altimeter/bmp3xx.h index 329b48f..5298648 100644 --- a/main/bmp_altimeter.h +++ b/main/sensors/altimeter/bmp3xx.h @@ -2,10 +2,12 @@ * This file is for all barometric altimeter queries * ****************************************************************************/ +#pragma once + #ifndef UNIT_TEST #include "Adafruit_BMP3XX.h" // BMP388 library #endif // UNIT_TEST -#include "utils.h" +#include "../../utils/datatypes.h" double GNDLEVELPRESSURE_HPA; @@ -51,7 +53,7 @@ void setupBMP() { XBeeSerial.print("Setting up BMP388... "); delay(3000); - if (!bmp.begin_SPI(BMP_CS)) { // hardware I2C mode, can pass in address & alt Wire + if (!bmp.begin_SPI(BMP_CS, SPI1_SCK, SPI1_MISO, SPI1_MOSI)) { XBeeSerial.println("could not find a valid BMP388 sensor, check wiring!"); return; } diff --git a/main/gps.h b/main/sensors/gps/adafruit_gps.h similarity index 91% rename from main/gps.h rename to main/sensors/gps/adafruit_gps.h index 785d2d3..ef47da2 100644 --- a/main/gps.h +++ b/main/sensors/gps/adafruit_gps.h @@ -1,8 +1,11 @@ // Test code for Ultimate GPS Using Hardware Serial. // Need to remove the GPS.lastNMEA() and see if it still works, still get compilation error +#pragma once + +#include "../../utils/datatypes.h" + #include -#include "utils.h" // what's the name of the hardware serial port? #define ERR_NO_GPS "flightData-noDate" @@ -23,14 +26,14 @@ String setupGPS() unsigned int startTime = millis(); // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 GPS.begin(9600); // 9600 is the default baud rate - GPS.sendCommand("$PMTK251,38400*27"); //set baud rate to 38400 - GPSSerial.end(); - delay(1000); - GPS.begin(38400); // set serial to 38400 + // GPS.sendCommand(PMTK_SET_BAUD_57600); //set baud rate to 115200 + // GPSSerial.end(); + // delay(1000); + // GPS.begin(57600); // set serial to 115200 delay(1000); // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); - GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate + GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ); // 5 Hz logging rate (update rate is independent of this) delay(1000); diff --git a/main/gy521_imu.h b/main/sensors/imu/gy521.cxx similarity index 91% rename from main/gy521_imu.h rename to main/sensors/imu/gy521.cxx index 25cbd27..d8d21af 100644 --- a/main/gy521_imu.h +++ b/main/sensors/imu/gy521.cxx @@ -1,22 +1,20 @@ /***************************************************************************** - * This file is for all imu queries + * This file is for all imu queries for the GY521 * Calculations such as attitude determination should go in a separate file ****************************************************************************/ -#ifndef UNIT_TEST -#include //library allows communication with I2C / TWI devices -#endif // UNIT_TEST + +#ifdef GY521 + +#include "imu.h" +#include "../../utils/utils.h" + #include //library includes mathematical functions -#include "utils.h" +#include //library allows communication with I2C / TWI devices const uint8_t MPU=0x68; //I2C address of the MPU-6050 const double tcal = -1600; //Temperature correction -/** - * @brief Sample the IMU (Gyroscope and Accelerometer) - * - * @return imuReading - */ -imuReading getIMU() +imuReading imu::sample() { imuReading imuSample; imuSample.time = micros(); //current timestamp @@ -56,8 +54,8 @@ imuReading getIMU() * * Verify that the connection is operating properly by taking a sample. */ -void setupIMU(){ - XBeeSerial.print("Setting up IMU... "); +void imu::setup(){ + XBeeSerial.print("Setting up GY521 IMU... "); Wire.beginTransmission(MPU); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2) Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28) @@ -75,7 +73,7 @@ void setupIMU(){ Wire.endTransmission(); delay(3000); // This delay allows time for the IMU setup to be completed before reading values. - imuReading sample = getIMU(); // Reads 1 sample of data from IMU. + imuReading sample = imu::sample(); // Reads 1 sample of data from IMU. if(sample.accel.x != 0 || sample.accel.y != 0 || sample.accel.z != 0) XBeeSerial.println("IMU setup successful."); @@ -103,3 +101,5 @@ XBeeSerial.println(sample.gyro.z); delay(1000); } + +#endif // GY521 diff --git a/main/sensors/imu/imu.h b/main/sensors/imu/imu.h new file mode 100644 index 0000000..9203d59 --- /dev/null +++ b/main/sensors/imu/imu.h @@ -0,0 +1,26 @@ +/********************************************** +* This is an interface for all IMU functions +* +**********************************************/ + +#pragma once + +#include "../../utils/datatypes.h" + +// Interface for IMU functions +class imu +{ + public: + /** + * @brief Sample the IMU (Gyroscope and Accelerometer) + * + * @return imuReading + */ + imuReading sample(); + + /** + * @brief setup the IMU + * + */ + void setup(); +}; diff --git a/main/sensors/imu/lsm6dsox.cxx b/main/sensors/imu/lsm6dsox.cxx new file mode 100644 index 0000000..62c1d17 --- /dev/null +++ b/main/sensors/imu/lsm6dsox.cxx @@ -0,0 +1,66 @@ +/***************************************************************************** + * This file is for all imu queries for the LSM6DSOX + * Calculations such as attitude determination should go in a separate file + * + * NOTE: Due to the current implementation this library, each flight computer + * can only have 1 LSM6DSOX IMU. + ****************************************************************************/ +#ifdef LSM6DSOX + +#include "imu.h" + +#include + +Adafruit_LSM6DSOX sox; + +/** + * @brief Sample the IMU (Gyroscope and Accelerometer) + * + * @return imuReading + */ +imuReading imu::sample() { + imuReading imuSample; + imuSample.time = micros(); //current timestamp + + sensors_event_t accel; + sensors_event_t gyro; + sensors_event_t temp; + + sox.getEvent(&accel, &gyro, &temp); + + // convert acceleration data from m/s^2 to Gs + // TODO: axis mapping should be controlled in config files (Z is up, Y and X are arbitrary for now) + imuSample.accel.x = accel.acceleration.x / 9.81; + imuSample.accel.z = accel.acceleration.y / 9.81; + imuSample.accel.y = accel.acceleration.z / 9.81; + + // TODO: axis mapping should be controlled in config files (Z is up, Y and X are arbitrary for now) + imuSample.gyro.x = gyro.gyro.x; + imuSample.gyro.z = gyro.gyro.y; + imuSample.gyro.y = gyro.gyro.z; + + (void) temp; //we can remove this if we decide to record temperature from the IMU + + return imuSample; //return gyro and accel data +} + +void imu::setup() { + XBeeSerial.print("Setting up IMU... "); + + if (!sox.begin_SPI(IMU_CS, SPI1_SCK, SPI1_MISO, SPI1_MOSI)) { + while (1) { + XBeeSerial.println("Failed to find LSM6DSOX chip, halting execution"); + delay(1000); + } + } + + sox.setAccelRange(LSM6DS_ACCEL_RANGE_16_G); + sox.setGyroRange(LSM6DS_GYRO_RANGE_2000_DPS); + sox.setAccelDataRate(LSM6DS_RATE_208_HZ); + sox.setGyroDataRate(LSM6DS_RATE_208_HZ); + + XBeeSerial.println("Done"); + +} + +#endif // LSM6DSOX diff --git a/main/sensors/radio/radio.h b/main/sensors/radio/radio.h new file mode 100644 index 0000000..f40193b --- /dev/null +++ b/main/sensors/radio/radio.h @@ -0,0 +1,39 @@ +/************************************************* +* This is an interface for all radio functions +* +*************************************************/ + +#pragma once + +#include + +class Radio +{ + public: + /** + * @brief Send a message to the ground station + * + * @param message + */ + void print(String message); + + /** + * @brief Send a message to the with an endline to the ground station + * + * @param message + */ + void println(String message); + + /** + * @brief Receive a message from the ground station + * + * @return String + */ + String receive(); + + /** + * @brief Setup the radio + * + */ + void setup(); +}; diff --git a/main/Ewma.h b/main/utils/Ewma.h similarity index 100% rename from main/Ewma.h rename to main/utils/Ewma.h diff --git a/main/utils.h b/main/utils/datatypes.h similarity index 51% rename from main/utils.h rename to main/utils/datatypes.h index a6a476a..196bef7 100644 --- a/main/utils.h +++ b/main/utils/datatypes.h @@ -1,10 +1,6 @@ -/***************************************************************************** - * This file is for declaring utilities to be used in multiple other places - * - ****************************************************************************/ #pragma once -#include "settings.h" +#include "../config/config.h" /** * @brief A 3 dimensional vector @@ -46,8 +42,8 @@ struct bmpReading struct imuReading { Directional accel; /**< Acceleration (measured in Gs)*/ - Directional gyro; /**< Rotational velocity (measured in degrees/s)*/ - Directional attitude; /**< Rotational position (measured in degrees)*/ + Directional gyro; /**< Rotational velocity (measured in rad/s)*/ + Directional attitude; /**< Rotational position (measured in radians)*/ uint32_t time; /**< Time of sampling in microseconds*/ }; @@ -62,26 +58,6 @@ struct gpsReading uint32_t time; /**< Time of sampling in microseconds*/ }; -/** - * @brief Convert radians to degrees - * - * @param angle Angle in radians - * @return double Angle in degrees - */ -double toDeg(double angle){ - return ((angle) *(180/PI)); -} - -/** - * @brief Convert degrees to radians - * - * @param angle Angle in degrees - * @return double Angle in radians - */ -double toRad(double angle){ - return ((angle) * (PI/180)); -} - /** * @brief Phases of Flight * @@ -89,23 +65,3 @@ double toRad(double angle){ * */ enum flightPhase {ONPAD, ASCENDING, DESCENDING, POST_FLIGHT}; - -/** - * @brief Get the tick time based on the current flight phase - * - * @param phase Flight phase - * @return int Tick length in microseconds - */ -int getTickTime(flightPhase phase){//map flight phases to tick times - switch(phase){ - case ONPAD: - return TICK_TIME_ONPAD; - case ASCENDING: - return TICK_TIME_ASCENDING; - case DESCENDING: - return TICK_TIME_DESCENDING; - case POST_FLIGHT: - return TICK_TIME_POST_FLIGHT; - } - return TICK_TIME_POST_FLIGHT; -} diff --git a/main/ringQueue.h b/main/utils/ringQueue.h similarity index 91% rename from main/ringQueue.h rename to main/utils/ringQueue.h index 065e1a6..1167455 100644 --- a/main/ringQueue.h +++ b/main/utils/ringQueue.h @@ -2,10 +2,9 @@ * This file is for the RingQueue class and definitions * ****************************************************************************/ -#include "settings.h" +#pragma once -#ifndef RINGQUEUE_H -#define RINGQUEUE_H +#include "config/config.h" /** * @brief A ring queue of constant size @@ -16,7 +15,7 @@ * * @tparam T Datatype to be stored */ -template +template class RingQueue{ public: RingQueue(); @@ -35,7 +34,7 @@ class RingQueue{ * * @tparam T Datatype to be stored */ -template +template RingQueue::RingQueue(){//each new queue is initialized with 0 data head = 0; length = 0; @@ -48,7 +47,7 @@ RingQueue::RingQueue(){//each new queue is initialized with 0 data * @return true RingQueue is empty * @return false RingQueue is NOT empty */ -template +template bool RingQueue::isEmpty(){ return length == 0;//if length is 0 it's empty otherwise it isn't } @@ -59,7 +58,7 @@ bool RingQueue::isEmpty(){ * @tparam T Type of data stored * @param data Value of data to be queued */ -template +template void RingQueue::enqueue(T data){ if(length >= RING_QUEUE_LENGTH) //if the array is full just ignore return; @@ -78,7 +77,7 @@ void RingQueue::enqueue(T data){ * @tparam T Type of data * @return T Data at front of queue */ -template +template T RingQueue::peek(){//unintended behaviour if called when queue is empty return dataArray[head]; } @@ -88,12 +87,10 @@ T RingQueue::peek(){//unintended behaviour if called when queue is empty * * @tparam T Type of data stored */ -template +template void RingQueue::dequeue(){ if(length == 0) return; length--; head++; } - -#endif diff --git a/main/utils/utils.cpp b/main/utils/utils.cpp new file mode 100644 index 0000000..3a7c52b --- /dev/null +++ b/main/utils/utils.cpp @@ -0,0 +1,24 @@ +#include "../config/config.h" +#include "utils.h" + +double toDeg(double angle){ + return ((angle) *(180/PI)); +} + +double toRad(double angle){ + return ((angle) * (PI/180)); +} + +int getTickTime(flightPhase phase){//map flight phases to tick times + switch(phase){ + case ONPAD: + return TICK_TIME_ONPAD; + case ASCENDING: + return TICK_TIME_ASCENDING; + case DESCENDING: + return TICK_TIME_DESCENDING; + case POST_FLIGHT: + return TICK_TIME_POST_FLIGHT; + } + return TICK_TIME_POST_FLIGHT; +} diff --git a/main/utils/utils.h b/main/utils/utils.h new file mode 100644 index 0000000..3c5fd14 --- /dev/null +++ b/main/utils/utils.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * This file is for declaring utilities to be used in multiple other places + * + ****************************************************************************/ +#pragma once + +#include "datatypes.h" + +/** + * @brief Convert radians to degrees + * + * @param angle Angle in radians + * @return double Angle in degrees + */ +double toDeg(double angle); + +/** + * @brief Convert degrees to radians + * + * @param angle Angle in degrees + * @return double Angle in radians + */ +double toRad(double angle); + +/** + * @brief Get the tick time based on the current flight phase + * + * @param phase Flight phase + * @return int Tick length in microseconds + */ +int getTickTime(flightPhase phase); diff --git a/platformio.ini b/platformio.ini index 32925bb..23ea24c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,9 +10,9 @@ [platformio] src_dir = main -default_envs = teensy41, pico +default_envs = catalyst2 -[env:teensy41] +[env:catalyst2] platform = teensy board = teensy41 framework = arduino @@ -21,18 +21,23 @@ lib_deps = adafruit/Adafruit BMP3XX Library@^2.1.2 adafruit/Adafruit GPS Library@^1.7.0 test_ignore = * +build_flags = + -D CATALYST2 + -D GY521 -[env:pico] -platform = raspberrypi +[env:birdbrain] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = pico framework = arduino +board_build.core = earlephilhower lib_deps = adafruit/Adafruit BMP3XX Library@^2.1.2 adafruit/Adafruit GPS Library@^1.7.0 - arduino-libraries/SD@1.2.4 + adafruit/Adafruit LSM6DS@^4.7.0 test_ignore = * build_flags = - -D PICO + -D BIRDBRAIN + -D LSM6DSOX [env:native] platform = native diff --git a/test/lib/mock_arduino.h b/test/lib/mock_arduino.h index de3fc87..6370f6a 100644 --- a/test/lib/mock_arduino.h +++ b/test/lib/mock_arduino.h @@ -28,6 +28,14 @@ class Adafruit_BMP3XX return true; } + bool begin_SPI(int chipSelect, int sck, int miso, int mosi) { + (void)chipSelect; + (void)sck; + (void)miso; + (void)mosi; + return true; + } + void setTemperatureOversampling(int setting) { (void)setting; } @@ -49,41 +57,25 @@ class Adafruit_BMP3XX }; -class TwoWire +class MockTwoWire { public: - uint8_t read() { - return 0b1000; // 8 - } + uint8_t read(); - void write(int message) { - (void)message; - } + void write(int message); - void beginTransmission(int address) { - (void)address; - } + void beginTransmission(int address); - void endTransmission(bool torf = false) { - (void)torf; - } + void endTransmission(bool torf); - void requestFrom(int address, int style, bool idk) { - (void)address; - (void)style; - (void)idk; - } + void requestFrom(int address, int style, bool idk); }; -TwoWire Wire; +extern MockTwoWire Wire; -unsigned long micros() { - static unsigned long micros = 1; - micros += 10; - return micros; -} +unsigned long micros(); -class Serial +class MockSerial { public: void print(std::string s) { @@ -99,8 +91,6 @@ class Serial } }; -Serial Serial1; +extern MockSerial Serial1; -void delay(int millis) { - (void)millis; -} \ No newline at end of file +void delay(int millis); diff --git a/test/test_bmp_altimeter/test_main.cpp b/test/test_bmp_altimeter/test_main.cpp index 790cde1..02bece6 100644 --- a/test/test_bmp_altimeter/test_main.cpp +++ b/test/test_bmp_altimeter/test_main.cpp @@ -1,5 +1,24 @@ #include -#include "../main/bmp_altimeter.h" + +#define XBeeSerial Serial1 +#define BMP_CS 10 +#define SPI1_SCK 11 +#define SPI1_MOSI 12 +#define SPI1_MISO 13 + +#include "../main/sensors/altimeter/bmp3xx.h" + +MockSerial Serial1; + +void delay(int millis) { + (void)millis; +} + +unsigned long micros() { + static unsigned long micros = 1; + micros += 10; + return micros; +} class BmpAltimeterTest : public ::testing::Test { protected: diff --git a/test/test_gy521_imu/test_main.cpp b/test/test_gy521_imu/test_main.cpp deleted file mode 100644 index c715582..0000000 --- a/test/test_gy521_imu/test_main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include "../main/gy521_imu.h" - -/// @brief test setupIMU -/// TODO: set up system to verify logs -TEST(IMUTest, SetupTest) -{ - setupIMU(); -} - -/// @brief test getIMU -TEST(IMUTest, SampleIMUTest) -{ - imuReading sample = getIMU(); - - EXPECT_NEAR(0.0000001, 0.501953125, sample.accel.x); - EXPECT_NEAR(0.0000001, 0.501953125, sample.accel.y); - EXPECT_NEAR(0.0000001, 0.501953125, sample.accel.z); - EXPECT_NEAR(0.0000001, 0.273923431, sample.gyro.x); - EXPECT_NEAR(0.0000001, 0.273923431, sample.gyro.y); - EXPECT_NEAR(0.0000001, 0.273923431, sample.gyro.z); - EXPECT_EQ(21, sample.time); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - if (RUN_ALL_TESTS()) - ; - // Always return zero-code and allow PlatformIO to parse results - return 0; -} \ No newline at end of file diff --git a/test/test_imu/mock_imu.cpp b/test/test_imu/mock_imu.cpp new file mode 100644 index 0000000..12923bc --- /dev/null +++ b/test/test_imu/mock_imu.cpp @@ -0,0 +1,22 @@ +/***************************************************************************** + * This file is for all imu queries for the a mock IMU + ****************************************************************************/ + +#include "../../main/sensors/imu/imu.h" + +imuReading imu::sample() +{ + imuReading imuSample; + imuSample.time = 5; //current timestamp + + imuSample.accel.x = 1; + imuSample.accel.y = 2; + imuSample.accel.z = 3; + imuSample.gyro.x = 4; + imuSample.gyro.y = 5; + imuSample.gyro.z = 6; + + return imuSample; //return gyro and accel data +} + +void imu::setup() {} diff --git a/test/test_imu/test_main.cpp b/test/test_imu/test_main.cpp new file mode 100644 index 0000000..edc83a1 --- /dev/null +++ b/test/test_imu/test_main.cpp @@ -0,0 +1,35 @@ +#include +#include "../main/sensors/imu/imu.h" + +/// @brief test setupIMU +TEST(IMUTest, SetupTest) +{ + imu imu; + + imu.setup(); +} + +/// @brief test getIMU +TEST(IMUTest, SampleIMUTest) +{ + imu imu; + + imuReading sample = imu.sample(); + + EXPECT_NEAR(0.0000001, 1, sample.accel.x); + EXPECT_NEAR(0.0000001, 2, sample.accel.y); + EXPECT_NEAR(0.0000001, 3, sample.accel.z); + EXPECT_NEAR(0.0000001, 4, sample.gyro.x); + EXPECT_NEAR(0.0000001, 5, sample.gyro.y); + EXPECT_NEAR(0.0000001, 6, sample.gyro.z); + EXPECT_EQ(5, sample.time); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if (RUN_ALL_TESTS()) + ; + // Always return zero-code and allow PlatformIO to parse results + return 0; +} \ No newline at end of file diff --git a/test/test_utils/mock_arduino.cpp b/test/test_utils/mock_arduino.cpp new file mode 100644 index 0000000..b5d1cd9 --- /dev/null +++ b/test/test_utils/mock_arduino.cpp @@ -0,0 +1,37 @@ +#include "./lib/mock_arduino.h" + +void delay(int millis) { + (void)millis; +} + +unsigned long micros() { + static unsigned long micros = 1; + micros += 10; + return micros; +} + +uint8_t MockTwoWire::read() { + return 0b1000; // 8 +} + +void MockTwoWire::write(int message) { + (void)message; +} + +void MockTwoWire::beginTransmission(int address) { + (void)address; +} + +void MockTwoWire::endTransmission(bool torf = false) { + (void)torf; +} + +void MockTwoWire::requestFrom(int address, int style, bool idk) { + (void)address; + (void)style; + (void)idk; +} + +MockTwoWire Wire; + +MockSerial Serial1; diff --git a/test/test_utils/mock_utils.cpp b/test/test_utils/mock_utils.cpp new file mode 100644 index 0000000..947b068 --- /dev/null +++ b/test/test_utils/mock_utils.cpp @@ -0,0 +1,14 @@ +#include "../main/utils/utils.h" + +double toDeg(double angle){ + return ((angle) *(180/PI)); +} + +double toRad(double angle){ + return ((angle) * (PI/180)); +} + +int getTickTime(flightPhase phase){//map flight phases to tick times + (void)phase; + return 10; +} diff --git a/test/test_utils/test_main.cpp b/test/test_utils/test_main.cpp index 505bddd..e7b26f3 100644 --- a/test/test_utils/test_main.cpp +++ b/test/test_utils/test_main.cpp @@ -1,12 +1,12 @@ #include -#include "../main/utils.h" +#include "../main/utils/utils.h" /// @brief test conversion of degrees to radians TEST(utils, degToRad) { double degrees = 60; double radians = toRad(degrees); - EXPECT_NEAR(0.0000001, 1.04719755, radians); + EXPECT_NEAR(0.0000001, 1.04719755, radians); // 60 degrees is about 1.047 radians } int main(int argc, char **argv)