* Project: Go For Launch
* Author: Jeren Arellano Reeder
* Date: 8/4/2025
* For comprehensive documentation and examples, please visit:
* https://docs.particle.io/firmware/best-practices/firmware-template/
*/
// Include Particle Device OS APIs
#include "Particle.h"
#include "Adafruit_BME280.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_GFX.h"
#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "neopixel.h"
#include "colors.h"
#include "button.h"
#include <Encoder.h>
#include "Adafruit_GPS.h"
#include "credentials.h"
// download GPS_CNM library
TCPClient TheClient;
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY);
Adafruit_MQTT_Publish pubFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/buttonPressDuration");
Adafruit_MQTT_Publish pubFeed1 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/latitude");
Adafruit_MQTT_Publish pubFeed2 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/longitude");
Adafruit_MQTT_Publish pubFeed3 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/altitude");
Adafruit_MQTT_Publish pubFeed4 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/tempature1");
Adafruit_MQTT_Publish pubFeed5 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/tempature");
Adafruit_MQTT_Publish pubFeed6 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/humidity");
Adafruit_MQTT_Publish pubFeed7 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/pressure");
Adafruit_MQTT_Publish pubFeed8 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/altChangeDuringPress");
// Adafruit_MQTT_Subscribe subFeed = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/balloonbutton");
void getGPS(float *latitude, float *longitude, float *altitude, int *satellites);
const int OLED_RESET=-1;
Adafruit_GPS GPS(&Wire);
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_SSD1306 display1(OLED_RESET);
Encoder myEnc(D11,D12);
Button encBut(D13);
SYSTEM_MODE(MANUAL);
float subValue,pubValue,pubValue1,pubValue2,pubValue3,pubValue4,pubValue5,pubValue6,pubValue7,pubValue8;
// Run the application and system concurrently in separate threads
SYSTEM_THREAD(ENABLED);
char number = 164;
const int TIMEZONE = -6;
const unsigned int UPDATE = 10000;
int n;
//neopixel
const int PIXELCOUNT = 13;
void pixelFill(int startpixel, int endpixel, int pixelColor);
Adafruit_NeoPixel pixel(PIXELCOUNT, SPI1, WS2812B);
// Blue Button for relay
const int buttonPin = D3;
//relay
const int relayPin = D6;
int inputValue;
unsigned int lastPublish;
// Declare Variables
int theHour;
int maxAlt = 9800; // max ballon height relative to abq sea level in meters (max ride height)
int pixelsOn;
int startPixel;
int endPixel;
Adafruit_BME280 bme;
int hexAddress = 0x76;
float temp, pres, humid, Fahrenheit, inHg;
bool status;
Adafruit_BME280 bme1;
int hexAddress1 = 0x77;
float temp1, pres1, humid1, Fahrenheit1, inHg1;
bool status1;
//button timer and duration
unsigned long buttonPressStart = 0;
unsigned long buttonPressDuration = 0;
bool buttonWasPressed = false;
float altAtButtonPress = 0.0;
//altutude
float lat;
float lon;
float alt;
int sat;
unsigned int lastGPS;
float previousAlt;
float altitudeDrop;
//functions
void MQTT_connect();
bool MQTT_ping();
void getGPS(float *latitude, float *longitude, float *altitude, int *satellites) ;
void setup() {
// pinMode(ledPin, OUTPUT);
pinMode(relayPin, OUTPUT);
pinMode(buttonPin, INPUT);
Serial.begin(9600);
waitFor(Serial.isConnected, 10000);
WiFi.on();
WiFi.connect();
while(WiFi.connecting()) {
Serial.printf(".");
}
Serial.printf("\n\n");
// mqtt.subscribe(&subFeed);
// while(!Serial);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display1.begin(SSD1306_SWITCHCAPVCC, 0x3D);
display.clearDisplay();
display1.clearDisplay();
GPS.begin(0x10); // The I2C address to use is 0x10
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
status = bme.begin(hexAddress);
if (status == TRUE) {
Serial.printf("BME280 at address 0x%02X started successfully!\n",hexAddress);
}
status = bme1.begin(hexAddress1);
if (status == TRUE) {
Serial.printf("BME280 at address 0x%02X started successfully!\n",hexAddress1);
}
pixel.begin();
pixel.setBrightness(40);
pixel.show();
display.display();
display1.display();
}
void loop() {
inputValue = digitalRead(buttonPin);
//gps
GPS.read();
if (GPS.newNMEAreceived()) {
if (!GPS.parse(GPS.lastNMEA())) { //cant parse/ retry
return;
}
}
//ADAFRUIT THRUSTBUTTON
// Adafruit_MQTT_Subscribe *subscription;
// while ((subscription = mqtt.readSubscription(100))) {
// if (subscription == &subFeed) {
// subValue = atof((char *)subFeed.lastread);
// if (subValue == 1) {
// digitalWrite(relayPin, HIGH);
// Serial.println("Relaystate = ON");
// }
// }
// }
if (millis() - lastGPS > UPDATE) { //if it can parse go here
lastGPS = millis(); // reset the timer
getGPS(&lat,&lon,&alt,&sat);
Serial.printf("\n=================================================================\n");
Serial.printf("Lat: %0.6f, Lon: %0.6f, Alt: %0.6f, Satellites: %i\n",lat, lon, alt, sat);
Serial.printf("Pressure %f\nHumidity %f\nFahrenheit %f\n",inHg,humid,Fahrenheit);
Serial.printf("=================================================================\n\n");
theHour = GPS.hour + TIMEZONE;
if(theHour < 0) {
theHour = theHour + 24;
}
//DISPLAYS
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.setRotation(0);
display.printf("Lat: %0.6f\n", lat);
display.printf("Lon: %0.6f\n", lon);
display.printf("Alt: %0.2f ft.(ASL)", alt);
display.printf("Satellites: %d\n", sat);
display.printf("Spd: %0.2f (Knots)\n", GPS.speed / 1.944);
display.printf("Balloon Pres: %0.1f\nBalloon Humidity:%0.1fBalloon Temp: %0.1fF\n",inHg,humid,Fahrenheit);
display.display();
display1.clearDisplay();
display1.setTextSize(1);
display1.setTextColor(WHITE);
display1.setCursor(0,0);
display1.setRotation(0);
display1.printf("Time: %02i:%02i:%02i\n", theHour, GPS.minute, GPS.seconds);
display1.printf("Thrust Duration(s):\n");
display1.printf("\nAltitude Change(ft):\n");
display1.printf("\nTemp vv Button >> \n");
display1.printf("Outside Temp %0.1fF\nBalloon Temp %0.1f \n",Fahrenheit1,Fahrenheit);
display1.display();
}
if (Fahrenheit > 115 ){
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.setRotation(0);
display.printf("\n BALLOON\n TEMP \n WARNING");
display.display();
}else{
if (Fahrenheit < 99.9 )
display.clearDisplay();
}
if (inputValue == HIGH) {
digitalWrite(relayPin, HIGH);
// thrust button is pressed
if (!buttonWasPressed) {
buttonPressStart = millis(); // Start the timer
altAtButtonPress = alt;
buttonWasPressed = true;
}
} else {
digitalWrite(relayPin, LOW);
// If the button was just released
if (buttonWasPressed) {
buttonPressDuration = millis() - buttonPressStart;
float altChangeDuringPress = alt - altAtButtonPress;
Serial.printf("Thrust Duration(s): %.2f\n", buttonPressDuration / 1000.0);
Serial.printf("Altitude Change(ft): %.2f\n", altChangeDuringPress);
Serial.printf("Temp(F) %0.1f\n", Fahrenheit);
display1.clearDisplay();
display1.setTextSize(1);
display1.setTextColor(WHITE);
display1.setCursor(0,0);
display1.setRotation(0);
display1.printf("Time: %02i:%02i:%02i\n", theHour, GPS.minute, GPS.seconds);
display1.printf("Thrust Duration(s): \n%.2f\n", buttonPressDuration / 1000.0);
display1.printf("Altitude Change(ft): %.2f\n", altChangeDuringPress);
display1.printf("Outside Temp(F) %0.1f\n", Fahrenheit1);
display1.printf("Balloon Temp(F) %0.1f\n", Fahrenheit);
display1.display();
pubValue = buttonPressDuration / 1000.0;
pubValue8 = altChangeDuringPress;
pubFeed.publish(pubValue);
pubFeed8.publish(pubValue8);
Serial.printf("Publishing %0.2f \n",pubValue);
Serial.printf("Publishing %0.2f \n",pubValue8);
buttonWasPressed = false;
}
}
altitudeDrop = previousAlt - alt;
if (altitudeDrop > 150) {
Serial.printf("Descending To %.2f meters!!!\n", altitudeDrop);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.setRotation(0);
display.printf("Descending To %.2f meters!!!\n", altitudeDrop);
display.display();
}
previousAlt = alt;
temp = bme.readTemperature();
pres = bme.readPressure();
humid = bme.readHumidity();
Fahrenheit = (temp * 9/5) + 32;
inHg = (pres * 0.00029529983071445);
temp1 = bme1.readTemperature();
pres1 = bme1.readPressure();
humid1 = bme1.readHumidity();
Fahrenheit1 = (temp1 * 9/5) + 32;
inHg1 = (pres1 * 0.00029529983071445);
//neopixel calculate
pixelsOn = alt * PIXELCOUNT / maxAlt;
// pixelsOn = ((alt / maxAlt) * PIXELCOUNT) + 1;
pixel.setPixelColor(0, blue);
if (pixelsOn > PIXELCOUNT) {
pixelsOn = PIXELCOUNT;
}
if (pixelsOn < 0) {
pixelsOn = 0;
}
for (n = 1; n < PIXELCOUNT; n++) {
startPixel = n;
endPixel = n;
if (n < pixelsOn) {
pixelFill(startPixel, endPixel, green);
} else {
pixelFill(startPixel, endPixel, 0);
}
}
if((millis()-lastPublish > 30000)) {
if(mqtt.Update()) {
// pubValue = buttonPressDuration;
pubValue1 = lat;
pubValue2 = lon;
pubValue3 = alt;
pubValue4 = Fahrenheit1;
pubValue5 = Fahrenheit;
pubValue6 = humid;
pubValue7 = inHg;
// pubValue8 = altChangeDuringPress;
// pubFeed.publish(pubValue);
pubFeed1.publish(pubValue1);
pubFeed2.publish(pubValue2);
pubFeed3.publish(pubValue3);
pubFeed4.publish(pubValue4);
pubFeed5.publish(pubValue5);
pubFeed6.publish(pubValue6);
pubFeed7.publish(pubValue7);
// pubFeed8.publish(pubValue8);
// Serial.printf("Publishing %0.2f \n",pubValue);
Serial.printf("Publishing %0.6f \n",pubValue1);
Serial.printf("Publishing %0.6f \n",pubValue2);
Serial.printf("Publishing %0.2f \n",pubValue3);
Serial.printf("Publishing %0.2f \n",pubValue4);
Serial.printf("Publishing %0.2f \n",pubValue5);
Serial.printf("Publishing %0.2f \n",pubValue6);
Serial.printf("Publishing %0.2f \n",pubValue7);
// Serial.printf("Publishing %0.2f \n",pubValue8);
}
lastPublish = millis();
}
}
void getGPS(float *latitude, float *longitude, float *altitude, int *satellites){
int theHour;
theHour = GPS.hour + TIMEZONE;
if(theHour < 0) {
theHour = theHour + 24;
}
Serial.printf("Time: %02i:%02i:%02i:%03i\n",theHour, GPS.minute, GPS.seconds, GPS.milliseconds);
Serial.printf("Dates: %02i-%02i-20%02i\n", GPS.month, GPS.day, GPS.year);
Serial.printf("Fix: %i, Quality: %i",(int)GPS.fix,(int)GPS.fixquality);
if (GPS.fix) { //gps fix returns a 0 if we are unable to get any data from a satalite 1 if you connected
*latitude = GPS.latitudeDegrees;
*longitude = GPS.longitudeDegrees;
*altitude = GPS.altitude;
*satellites = (int)GPS.satellites; //tightcasting this data type to be a integer
Serial.printf("Lat: %0.6f, Lon: %0.6f, Alt: %0.6f\n",*latitude, *longitude, *altitude);
Serial.printf("Speed (Knots): %0.2f\n",GPS.speed);
Serial.printf("Angle: %0.2f\n",GPS.angle);
Serial.printf("Satellites: %i\n",*satellites);
}
}
void pixelFill(int startpixel, int endpixel, int pixelColor){
for(int n = startpixel; n <= endpixel; n++){
pixel.setPixelColor(n, pixelColor);
}
pixel.show();
}
Comments