Devices & Components
Grove - I2C Hub (6 Port)
Charger : TP4056 : Type C USB 5V 1A 18650
GPS Antenna
Geiger Counter Module Ionizing Radiation Detector
Camera Module
SD card to SPI adaptor
GNSS GPS BeiDou Positioning Module with RTC - I2C&UART
Solar Lipo Charger (3.7V)
micro switch
FireBeetle 2 Board ESP32-S3-U
Jumper wire cables (male / female 100mm & 200mm)
M3 Brass Standoffs
Gravity: Analog Ambient Light Sensor TEMT6000
Zero PCB
2x5mm Screws
I2C 3.7V Li Battery Fuel Gauge
4GB or Larger Micro SD Card
Machifit Aluminum Idler Pulley Plate
PETG-CF
M2 nut
Male Header Pins
10 DOF IMU AHRS BNO055 + BMP280
Aluminium foil
Gravity: Analog Sound Sensor For Arduino
Hardware & Tools
Screwdrivers
cables
Soldering Station
Computer
Software & Tools
VSCODE
Bambu Lab
FreeCAD
Unreal Engine
Arduino IDE
Twinmotion
InfluxDB
Audacity
Blender
Fusion 360
Project description
Code
HC-Eleni
cpp
HeliosCube-Eleni: A Cost-Effective, Open-Source 1U
1/* 2 3#mpla-ExoTech 4#DLR 5 6HeliosCube-Eleni 7-A Cost-Effective, Open-Source 1U CubeSat for Microgravity Research 8 9@Petros Mand 10-Athens, 08/12025 11 12*/ 13 14#include <Wire.h> 15#include <SPI.h> 16#include <SD.h> 17#include "DFRobot_GNSS.h" 18#include "DFRobot_BNO055.h" 19#include "DFRobot_BMP280.h" 20#include "DFRobot_Geiger.h" 21 22#define SD_CS_PIN D7 23#define SEA_LEVEL_PRESSURE 1015.0f 24#define UV_SENSOR_PIN A4 25#define GEIGER_PIN D6 26#define MIC_PIN A5 27#define LOG_INTERVAL 100 28#define FLUSH_INTERVAL 100 29#define SAMPLE_RATE 48000 30#define SAMPLE_PERIOD (1000000UL / SAMPLE_RATE) 31 32#define DEBUG 1 33#if DEBUG 34 #define DEBUG_PRINT(x) Serial.println(x) 35#else 36 #define DEBUG_PRINT(x) 37#endif 38 39DFRobot_GNSS_I2C gnss(&Wire, GNSS_DEVICE_ADDR); 40typedef DFRobot_BNO055_IIC BNO; 41BNO bno(&Wire, 0x28); 42typedef DFRobot_BMP280_IIC BMP; 43BMP bmp(&Wire, BMP::eSdoLow); 44DFRobot_Geiger geiger(GEIGER_PIN); 45 46File dataFile; 47File alertFile; 48File audio8k; 49File audio12k; 50File audio16k; 51 52unsigned long lastLogTime = 0; 53unsigned long lastFlushTime = 0; 54unsigned long nextSampleTime = 0; 55 56bool liftOffDetected = false; 57float maxPressure = 0; 58bool maxQLogged = false; 59bool microgravityLogged = false; 60bool tropopauseLogged = false; 61bool stratosphereLogged = false; 62 63void logAlert(String message, sTim_t utc) { 64 alertFile = SD.open("/hcelenimissiondataalert.csv", FILE_APPEND); 65 if (alertFile) { 66 alertFile.print(utc.hour); alertFile.print(":"); 67 alertFile.print(utc.minute); alertFile.print(":"); 68 alertFile.print(utc.second); alertFile.print(","); 69 alertFile.println(message); 70 alertFile.close(); 71 DEBUG_PRINT("Alert: " + message); 72 } else { 73 DEBUG_PRINT("Error writing alert to SD!"); 74 } 75} 76 77String formatCSVData(sTim_t date, sTim_t utc, sLonLat_t lat, sLonLat_t lon, double altitude, float temp, uint32_t press, float baroAltitude, BNO::sAxisAnalog_t acc, BNO::sAxisAnalog_t gyr, BNO::sAxisAnalog_t mag, float uvIndex, int cpm, float nSv, float uSv, uint8_t sats, double sog, double cog, BNO::sEulAnalog_t eul) { 78 char buffer[512]; 79 snprintf(buffer, sizeof(buffer), "%04d/%02d/%02d,%02d:%02d:%02d,%.6f,%.6f,%.2f,%.2f,%u,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%.2f,%.2f,%u,%.2f,%.2f,%.2f,%.2f,%.2f", 80 date.year, date.month, date.date, utc.hour, utc.minute, utc.second, 81 lat.latitudeDegree, lon.lonitudeDegree, altitude, temp, press, baroAltitude, 82 acc.x, acc.y, acc.z, gyr.x, gyr.y, gyr.z, mag.x, mag.y, mag.z, 83 uvIndex, cpm, nSv, uSv, sats, sog, cog, eul.head, eul.roll, eul.pitch); 84 return String(buffer); 85} 86 87void setup() { 88 Serial.begin(115200); 89 90 while (!gnss.begin()) { 91 DEBUG_PRINT("GNSS: No Devices!"); 92 delay(100); 93 } 94 gnss.enablePower(); 95 gnss.setGnss(eGPS_BeiDou); 96 gnss.setRgbOn(); 97 DEBUG_PRINT("GNSS Initialized"); 98 99 bno.reset(); 100 while (bno.begin() != BNO::eStatusOK) { 101 DEBUG_PRINT("BNO: Initialization failed"); 102 delay(100); 103 } 104 DEBUG_PRINT("BNO Initialized"); 105 106 bmp.reset(); 107 while (bmp.begin() != BMP::eStatusOK) { 108 DEBUG_PRINT("BMP280: Initialization failed"); 109 delay(100); 110 } 111 DEBUG_PRINT("BMP280 Initialized"); 112 113 geiger.start(); 114 DEBUG_PRINT("Geiger Counter Started"); 115 116 if (!SD.begin(SD_CS_PIN)) { 117 DEBUG_PRINT("SD Card init failed!"); 118 while (1); 119 } 120 DEBUG_PRINT("SD Card Initialized"); 121 122 dataFile = SD.open("/hcelenimissiondatafile.csv", FILE_WRITE); 123 if (dataFile) { 124 dataFile.println("Date,Time,Latitude,Longitude,Altitude,Temp,Pressure,BaroAltitude,AccX,AccY,AccZ,GyrX,GyrY,GyrZ,MagX,MagY,MagZ,UVIndex,CPM,nSv/h,uSv/h,Sats,Speed,Heading,EulHead,EulRoll,EulPitch"); 125 dataFile.close(); 126 } else { 127 DEBUG_PRINT("Error creating data file!"); 128 } 129 130 audio8k = SD.open("/audio_8k.raw", FILE_WRITE); 131 if (!audio8k) DEBUG_PRINT("Error creating audio_8k file!"); 132 audio12k = SD.open("/audio_12k.raw", FILE_WRITE); 133 if (!audio12k) DEBUG_PRINT("Error creating audio_12k file!"); 134 audio16k = SD.open("/audio_16k.raw", FILE_WRITE); 135 if (!audio16k) DEBUG_PRINT("Error creating audio_16k file!"); 136 137 nextSampleTime = micros(); 138 lastFlushTime = millis(); 139 lastLogTime = millis(); 140} 141 142void loop() { 143 144 while (micros() < nextSampleTime); 145 nextSampleTime += SAMPLE_PERIOD; 146 147 int micValue = analogRead(MIC_PIN); 148 149 static int sum8k = 0, count8k = 0; 150 static int sum12k = 0, count12k = 0; 151 static int sum16k = 0, count16k = 0; 152 153 sum8k += micValue; count8k++; 154 sum12k += micValue; count12k++; 155 sum16k += micValue; count16k++; 156 157 if (count16k == 3) { 158 int avg = sum16k / 3; 159 uint8_t sample = map(avg, 0, 4095, 0, 255); 160 if (audio16k) audio16k.write(sample); 161 sum16k = 0; count16k = 0; 162 } 163 164 if (count12k == 4) { 165 int avg = sum12k / 4; 166 uint8_t sample = map(avg, 0, 4095, 0, 255); 167 if (audio12k) audio12k.write(sample); 168 sum12k = 0; count12k = 0; 169 } 170 171 if (count8k == 6) { 172 int avg = sum8k / 6; 173 uint8_t sample = map(avg, 0, 4095, 0, 255); 174 if (audio8k) audio8k.write(sample); 175 sum8k = 0; count8k = 0; 176 } 177 178 unsigned long currentMillis = millis(); 179 if (currentMillis - lastFlushTime >= FLUSH_INTERVAL) { 180 if (audio8k) audio8k.flush(); 181 if (audio12k) audio12k.flush(); 182 if (audio16k) audio16k.flush(); 183 lastFlushTime = currentMillis; 184 } 185 186 if (currentMillis - lastLogTime >= LOG_INTERVAL) { 187 lastLogTime = currentMillis; 188 189 sTim_t utc = gnss.getUTC(); 190 sTim_t date = gnss.getDate(); 191 sLonLat_t lat = gnss.getLat(); 192 sLonLat_t lon = gnss.getLon(); 193 double altitude = gnss.getAlt(); 194 float temp = bmp.getTemperature(); 195 uint32_t press = bmp.getPressure(); 196 float baroAltitude = bmp.calAltitude(SEA_LEVEL_PRESSURE, press); 197 BNO::sAxisAnalog_t acc = bno.getAxis(BNO::eAxisAcc); 198 BNO::sAxisAnalog_t gyr = bno.getAxis(BNO::eAxisGyr); 199 BNO::sAxisAnalog_t mag = bno.getAxis(BNO::eAxisMag); 200 int analogValue = analogRead(UV_SENSOR_PIN); 201 float uvIndex = (analogValue >= 20) ? min(0.05 * analogValue - 1.0, 11.0) : 0; 202 int cpm = geiger.getCPM(); 203 float nSv = geiger.getnSvh(); 204 float uSv = geiger.getuSvh(); 205 uint8_t sats = gnss.getNumSatUsed(); 206 double sog = gnss.getSog(); 207 double cog = gnss.getCog(); 208 BNO::sEulAnalog_t eul = bno.getEul(); 209 210 String csvData = formatCSVData(date, utc, lat, lon, altitude, temp, press, baroAltitude, acc, gyr, mag, uvIndex, cpm, nSv, uSv, sats, sog, cog, eul); 211 dataFile = SD.open("/hcelenimissiondatafile.csv", FILE_APPEND); 212 if (dataFile) { 213 dataFile.println(csvData); 214 dataFile.close(); 215 } else { 216 DEBUG_PRINT("Error writing to data file!"); 217 } 218 219 float accTotal = sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z); 220 221 if (!liftOffDetected && accTotal > 1.5) { 222 logAlert("LiftOff Detected", utc); 223 liftOffDetected = true; 224 } 225 226 if (press > maxPressure) { 227 maxPressure = press; 228 } else if (!maxQLogged && press < maxPressure * 0.98) { 229 logAlert("MaxQ Reached", utc); 230 maxQLogged = true; 231 } 232 if (!microgravityLogged && accTotal < 1.0) { 233 logAlert("Microgravity Detected", utc); 234 microgravityLogged = true; 235 } 236 if (!tropopauseLogged && altitude > 11000) { 237 logAlert("Tropopause Entered", utc); 238 tropopauseLogged = true; 239 } 240 if (!stratosphereLogged && altitude > 20000) { 241 logAlert("Stratosphere Entered", utc); 242 stratosphereLogged = true; 243 } 244 } 245}
Documentation
Presentation
HeliosCube-Eleni CubeSat Presentation
HeliosCube-Eleni CubeSat Presentation.pdf
Comments
Only logged in users can leave comments