diff --git a/src/ControlTasks/RadioControlTask.cpp b/src/ControlTasks/RadioControlTask.cpp index 07418e0..e4bc1b2 100644 --- a/src/ControlTasks/RadioControlTask.cpp +++ b/src/ControlTasks/RadioControlTask.cpp @@ -7,12 +7,11 @@ RadioControlTask::RadioControlTask() void RadioControlTask::init() { if (!sfr::radio::initialized) { - switch (sfr::radio::start_progress) { - case 0: + if (sfr::radio::start_progress == 0) { #ifdef VERBOSE - Serial.print(F("Radio: Initializing ... ")); + Serial.println(F("Radio: Initializing ... ")); #endif - // initialize SX1278 with default settings + // Initialize SX1278 with default settings code = radio.begin(constants::radio::freq, constants::radio::bw, constants::radio::sf, constants::radio::cr, constants::radio::sw, constants::radio::pwr, constants::radio::pl, constants::radio::gn); if (code == RADIOLIB_ERR_NONE) { @@ -23,12 +22,13 @@ void RadioControlTask::init() Serial.println(code); #endif } - break; - case 1: + } + + if (sfr::radio::start_progress == 1) { #ifdef VERBOSE - Serial.print(F("Radio: Setting CRC parameter ... ")); + Serial.println(F("Radio: Setting CRC parameter ... ")); #endif - // set CRC parameter to true so it matches the CRC parameter on the TinyGS side + // Set CRC parameter to true so it matches the CRC parameter on the TinyGS side code = radio.setCRC(true); if (code == RADIOLIB_ERR_NONE) { sfr::radio::start_progress++; @@ -38,39 +38,51 @@ void RadioControlTask::init() Serial.println(code); #endif } - break; - case 2: + + if (sfr::radio::start_progress == 2) { #ifdef VERBOSE - Serial.print(F("Radio: Setting forceLDRO parameter ... ")); + Serial.println(F("Radio: Setting forceLDRO parameter ... ")); #endif - // set forceLDRO parameter to true so it matches the forceLDRO parameter on the TinyGS side - code = radio.forceLDRO(true); - if (code == RADIOLIB_ERR_NONE) { - sfr::radio::start_progress++; - } else { + // Set forceLDRO parameter to true so it matches the forceLDRO parameter on the TinyGS side + code = radio.forceLDRO(true); + if (code == RADIOLIB_ERR_NONE) { + sfr::radio::initialized = true; + } else { #ifdef VERBOSE - Serial.print(F("failed, code ")); - Serial.println(code); + Serial.print(F("failed, code ")); + Serial.println(code); #endif + } } - break; - case 3: // completed initialization - sfr::radio::initialized = true; - break; } } } bool RadioControlTask::transmit(uint8_t *packet, uint8_t size) { + // Blink LED during transmit + if (millis() - sfr::gps::boot_time > constants::led::led_on_time) { + digitalWrite(constants::led::led_pin, HIGH); + } + +#ifdef VERBOSE uint32_t start = millis(); +#endif code = radio.transmit(packet, size); +#ifdef VERBOSE uint32_t time = millis() - start; +#endif + + if (millis() - sfr::gps::boot_time > constants::led::led_on_time) { + digitalWrite(constants::led::led_pin, LOW); + } +#ifdef VERBOSE Serial.print(F("Time to transmit (ms): ")); Serial.println(time); +#endif if (code == RADIOLIB_ERR_NONE) { - // the packet was successfully transmitted + // The packet was successfully transmitted #ifdef VERBOSE Serial.println(F("success!")); Serial.print(F("[SX1278] Datarate:\t")); @@ -81,10 +93,10 @@ bool RadioControlTask::transmit(uint8_t *packet, uint8_t size) } else { #ifdef VERBOSE if (code == RADIOLIB_ERR_TX_TIMEOUT) { - // timeout occurred while transmitting packet + // Timeout occurred while transmitting packet Serial.println(F("timeout!")); } else { - // some other error occurred + // Some other error occurred Serial.print(F("failed, code ")); Serial.println(code); } @@ -104,23 +116,13 @@ bool RadioControlTask::receive() // packet was successfully received Serial.println(F("success!")); - // print the data of the packet Serial.print(F("[SX1278] Data:\t\t\t")); - - // print the RSSI (Received Signal Strength Indicator) - // of the last received packet Serial.print(F("[SX1278] RSSI:\t\t\t")); Serial.print(radio.getRSSI()); Serial.println(F(" dBm")); - - // print the SNR (Signal-to-Noise Ratio) - // of the last received packet Serial.print(F("[SX1278] SNR:\t\t\t")); Serial.print(radio.getSNR()); Serial.println(F(" dB")); - - // print frequency error - // of the last received packet Serial.print(F("[SX1278] Frequency error:\t")); Serial.print(radio.getFrequencyError()); Serial.println(F(" Hz")); @@ -129,13 +131,13 @@ bool RadioControlTask::receive() } else { #ifdef VERBOSE if (code == RADIOLIB_ERR_RX_TIMEOUT) { - // timeout occurred while waiting for a packet + // Timeout occurred while waiting for a packet Serial.println(F("timeout!")); } else if (code == RADIOLIB_ERR_CRC_MISMATCH) { - // packet was received, but is malformed + // Packet was received, but is malformed Serial.println(F("CRC error!")); } else { - // some other error occurred + // Some other error occurred Serial.print(F("failed, code ")); Serial.println(code); } @@ -146,7 +148,7 @@ bool RadioControlTask::receive() void RadioControlTask::execute() { - // implements the state machine described in: https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki + // Implement the state machine described in https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki switch (sfr::radio::mode) { case radio_mode_type::init: { #ifdef VERBOSE @@ -154,6 +156,17 @@ void RadioControlTask::execute() #endif init(); if (sfr::radio::initialized) { + sfr::radio::mode = radio_mode_type::aliveSignal; + } + break; + } + case radio_mode_type::aliveSignal: { +#ifdef VERBOSE + Serial.println(F("Radio: Alive Signal State")); +#endif + normalReportDownlink(); + sfr::radio::alive_signal_dlinks++; + if (sfr::radio::alive_signal_dlinks == constants::radio::max_alive_signal_dlinks) { sfr::radio::mode = radio_mode_type::downlink; sfr::radio::listen_period_start = millis(); downlinkSettings(); @@ -164,24 +177,24 @@ void RadioControlTask::execute() #ifdef VERBOSE Serial.println(F("Radio: Downlink State")); #endif - // downlink when slot reached + // Downlink when slot reached if (!sfr::radio::downlinked_in_slot && millis() - sfr::radio::downlink_window_start >= constants::radio::transmit_slot_length * sfr::radio::downlink_slot) { executeDownlink(); sfr::radio::downlinked_in_slot = true; } - // go into listen mode if listen period reached + // Go into listen mode if listen period reached if (millis() - sfr::radio::listen_period_start >= constants::radio::listen_period) { sfr::radio::mode = radio_mode_type::listen; #ifdef VERBOSE Serial.println(F("Radio: Listen Flag Downlink")); #endif - // downlink with listen flag = true + // Downlink with listen flag = true normalReportDownlink(); sfr::radio::command_wait_start = millis(); } - // reset window and choose slot for next downlink + // Reset window and choose slot for next downlink else if (millis() - sfr::radio::downlink_window_start >= sfr::radio::downlink_window_length) { downlinkSettings(); } @@ -191,7 +204,7 @@ void RadioControlTask::execute() #ifdef VERBOSE Serial.println(F("Radio: Listen State")); #endif - // built in timeout is 100 LoRa symbols + // Built in timeout is 100 LoRa symbols bool receive_success = receive(); if (receive_success) { processUplink(); @@ -225,7 +238,7 @@ bool RadioControlTask::executeDownlink() if (millis() - sfr::radio::last_callsign_time < constants::radio::callsign_interval) { return normalReportDownlink(); } else { - uint8_t dlink[] = {'K', 'D', '2', 'W', 'T', 'Q'}; + uint8_t dlink[] = {'K', 'C', '3', 'V', 'A', 'T', 'K', 'D', '2', 'W', 'T', 'Q', 'K', 'E', '2', 'A', 'T', 'R'}; sfr::radio::last_callsign_time = millis(); #ifdef VERBOSE Serial.println(F("Callsign Report")); @@ -237,17 +250,18 @@ bool RadioControlTask::executeDownlink() bool RadioControlTask::normalReportDownlink() { - // see https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki/2.-Telemetry for more info + // See https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki/2.-Telemetry for more info uint16_t lat = sfr::gps::latitude * 100; uint16_t lon = sfr::gps::longitude * 100; uint16_t alt = sfr::gps::altitude / 10; uint8_t flags = 0; - flags |= constants::radio::id << 6; - flags |= sfr::gps::valid_msg << 5; // gps valid - flags |= sfr::imu::initialized << 4; // imu valid - flags |= sfr::gps::on << 3; // boot mode flag - flags |= (sfr::radio::mode == radio_mode_type::listen) << 2; // listen flag + flags |= constants::radio::id << 6; // chipsat ID # + flags |= sfr::gps::valid_location << 5; // gps position valid + flags |= sfr::gps::valid_altitude << 4; // gps altiude valid + flags |= sfr::imu::initialized << 3; // imu valid + flags |= sfr::gps::on << 2; // boot mode flag + flags |= (sfr::radio::mode == radio_mode_type::listen) << 1; // listen flag uint8_t dlink[] = { (uint8_t)lat, (uint8_t)(lat >> 8), @@ -286,7 +300,7 @@ uint8_t RadioControlTask::map_range(float value, int min_val, int max_val) void RadioControlTask::processUplink() { - // see https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki/3.-Commands for more info + // See https://github.com/Alpha-CubeSat/oop-chipsat-code/wiki/3.-Commands for more info switch (received[0]) { case constants::opcodes::no_op: // No-Op #ifdef VERBOSE diff --git a/src/ControlTasks/RadioControlTask.hpp b/src/ControlTasks/RadioControlTask.hpp index 6f9ed03..8daeb09 100644 --- a/src/ControlTasks/RadioControlTask.hpp +++ b/src/ControlTasks/RadioControlTask.hpp @@ -21,17 +21,17 @@ class RadioControlTask void init(); /** - * Resets the transmit window start time and picks a new slot + * @brief Resets the transmit window start time and picks a new slot */ void downlinkSettings(); /** - * @brief Wrapper around RadioLib's transmit() to provide metadata (TODO: Redundant?) + * @brief Wrapper around RadioLib's transmit() to provide metadata */ bool transmit(uint8_t *packet, uint8_t size); /** - * @brief Attempts to receive a 3 byte command (TODO: Redundant?) + * @brief Attempts to receive a 3 byte command */ bool receive(); diff --git a/src/MainControlLoop.cpp b/src/MainControlLoop.cpp index cf6b15a..ccf6445 100644 --- a/src/MainControlLoop.cpp +++ b/src/MainControlLoop.cpp @@ -61,9 +61,12 @@ void MainControlLoop::execute() Serial.print(F("GPS Altitude (m): ")); Serial.println(sfr::gps::altitude); - Serial.print("Downlink Slot: "); + Serial.print(F("Downlink Slot: ")); Serial.println(sfr::radio::downlink_slot); + Serial.print(F("Alive Time: ")); + Serial.println(millis() - sfr::gps::boot_time); + #endif temp_monitor.execute(); @@ -71,6 +74,11 @@ void MainControlLoop::execute() gps_monitor.execute(); radio_control_task.execute(); + // Turn off LED 5 seconds after boot + if (millis() - sfr::gps::boot_time > constants::led::led_on_time) { + digitalWrite(constants::led::led_pin, LOW); + } + wdt_reset(); #ifdef VERBOSE diff --git a/src/Modes/radio_mode_type.enum b/src/Modes/radio_mode_type.enum index 6a5d100..0d42e92 100644 --- a/src/Modes/radio_mode_type.enum +++ b/src/Modes/radio_mode_type.enum @@ -4,6 +4,7 @@ enum class radio_mode_type : unsigned char { init = 0, + aliveSignal, downlink, listen }; diff --git a/src/Monitors/GPSMonitor.cpp b/src/Monitors/GPSMonitor.cpp index b2113cf..1241a7f 100644 --- a/src/Monitors/GPSMonitor.cpp +++ b/src/Monitors/GPSMonitor.cpp @@ -9,7 +9,7 @@ void GPSMonitor::init() digitalWrite(constants::gps::reset_pin, LOW); sfr::gps::on = true; - // writes NMEA output message type to SRAM and Flash + // Writes NMEA output message type to SRAM and Flash uint8_t SetNMEA[] = { 0xA0, 0xA1, 0x00, 0x03, 0x09, 0x01, 0x01, 0x09, 0x0D, 0x0A}; @@ -19,6 +19,7 @@ void GPSMonitor::init() void GPSMonitor::execute() { + // Turn on GPS after boot mode is over (10 seconds) if (!sfr::gps::on) { if (millis() - sfr::gps::boot_time > constants::gps::boot_time) { init(); @@ -33,10 +34,8 @@ void GPSMonitor::execute() gps.encode(ss.read()); serial_reads++; } - // while (*gpsStream) { - // gps.encode(*gpsStream++); - // } + // Read GPS values if they are valid if (gps.location.isUpdated() && gps.location.isValid()) { sfr::gps::latitude = gps.location.lat(); sfr::gps::longitude = gps.location.lng(); @@ -50,6 +49,9 @@ void GPSMonitor::execute() sfr::gps::utc_time = gps.time.value(); } + sfr::gps::valid_location = gps.location.isValid(); + sfr::gps::valid_altitude = gps.altitude.isValid(); + #ifdef VERBOSE Serial.print(F("Chars=")); Serial.print(gps.charsProcessed()); diff --git a/src/Monitors/GPSMonitor.hpp b/src/Monitors/GPSMonitor.hpp index 0453dc0..7cd8abd 100644 --- a/src/Monitors/GPSMonitor.hpp +++ b/src/Monitors/GPSMonitor.hpp @@ -27,14 +27,9 @@ class GPSMonitor SoftwareSerial ss = SoftwareSerial(constants::gps::rx_pin, constants::gps::tx_pin); /** - * Begins the software serial instance and configures the GPS receiver + * @brief Begins the software serial instance and configures the GPS receiver */ void init(); - - // TODO: Test edge cases for lat, lon, alt - const char *gpsStream = "$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\r\n" - "$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\r\n" - "$GPGG@,045252.000,3014.4273,S,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\r\n"; }; #endif \ No newline at end of file diff --git a/src/Monitors/IMUMonitor.cpp b/src/Monitors/IMUMonitor.cpp index 1d8b422..f6dabe4 100644 --- a/src/Monitors/IMUMonitor.cpp +++ b/src/Monitors/IMUMonitor.cpp @@ -18,7 +18,6 @@ void IMUMonitor::execute() } else { sfr::imu::initialized = true; } - } else { capture_imu_values(); } @@ -26,25 +25,25 @@ void IMUMonitor::execute() void IMUMonitor::capture_imu_values() { - // check if the gyroscope, accelerometer, or magnetometer has new data available + // Check if the gyroscope, accelerometer, or magnetometer has new data available if (IMU.gyroscopeAvailable()) { IMU.readGyroscope( sfr::imu::gyro_x, sfr::imu::gyro_y, - sfr::imu::gyro_z); // data is in degrees/s + sfr::imu::gyro_z); // Data is in degrees/s } if (IMU.accelerationAvailable()) { IMU.readAcceleration( sfr::imu::acc_x, sfr::imu::acc_y, - sfr::imu::acc_z); // data is in m/s^2 + sfr::imu::acc_z); // Data is in m/s^2 } if (IMU.magneticFieldAvailable()) { IMU.readMagneticField( sfr::imu::mag_x, sfr::imu::mag_y, - sfr::imu::mag_z); // data in uT + sfr::imu::mag_z); // Data in uT } } \ No newline at end of file diff --git a/src/Monitors/TempMonitor.cpp b/src/Monitors/TempMonitor.cpp index 59144b2..2df3e1d 100644 --- a/src/Monitors/TempMonitor.cpp +++ b/src/Monitors/TempMonitor.cpp @@ -13,11 +13,10 @@ void TempMonitor::execute() // Select no hold master Wire.write(0xF3); Wire.endTransmission(); - delay(300); // TODO: Look at this delay + delay(300); // TODO: Look at this delay (for real) // Request 2 bytes of data Wire.requestFrom(constants::temperature::i2c_address, 2); - // TODO: Delay here? if (Wire.available() == 2) { data[0] = Wire.read(); diff --git a/src/constants.hpp b/src/constants.hpp index 7a512ce..4cf7f89 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -13,14 +13,16 @@ namespace constants { constexpr int radio_rst_pin = 5; constexpr int radio_busy_pin = 16; - constexpr float freq = 433.0; - constexpr float bw = 125.0; - constexpr int sf = 12; - constexpr int cr = 5; - constexpr int sw = 18; - constexpr int pwr = 20; - constexpr int pl = 8; - constexpr int gn = 0; + constexpr float freq = 437.4; // MHz + constexpr float bw = 62.5; // kHz + constexpr int sf = 10; // Between 7 and 12 + constexpr int cr = 5; // Between 5 and 8. 4/8 coding ratio. One redundancy bit for every data bit + constexpr int sw = 18; // Sync-word (defines network). Default is 0d18 + constexpr int pwr = 20; // Between 2 and 17, or 20 for max power + constexpr int pl = 8; // Payload length + constexpr int gn = 0; // Gain + + constexpr uint8_t max_alive_signal_dlinks = 3; #ifdef CHIPSAT_ID constexpr uint8_t id = CHIPSAT_ID; @@ -28,19 +30,19 @@ namespace constants { constexpr uint8_t id = 0; #endif - constexpr uint32_t listen_period = 30 * constants::time::one_second; + constexpr uint32_t listen_period = 30 * constants::time::one_minute; constexpr uint32_t command_wait_period = 30 * constants::time::one_second; constexpr uint32_t callsign_interval = 10 * constants::time::one_minute; - constexpr uint32_t transmit_slot_length = 2 * constants::time::one_second; - + constexpr uint32_t transmit_slot_length = 700; // ms + // TODO: Verify sensor data on alive signal } // namespace radio namespace imu { - constexpr int gyro_min = -245; // default gyro range is +/- 245 dps + constexpr int gyro_min = -245; // Default gyro range is +/- 245 dps constexpr int gyro_max = 245; - constexpr int acc_min = -20; // default accel range is +/- 2 G + constexpr int acc_min = -20; // Default accel range is +/- 2 G constexpr int acc_max = 20; - constexpr int mag_min = -100; // default mag range is +/- 4 guass -> 100 uT + constexpr int mag_min = -100; // Default mag range is +/- 4 guass -> 100 uT constexpr int mag_max = 100; } // namespace imu namespace temperature { @@ -55,13 +57,16 @@ namespace constants { constexpr int reset_pin = 7; constexpr int buffer_size = 11; - constexpr uint32_t boot_time = constants::time::one_second * 30; + constexpr uint32_t boot_time = 10 * constants::time::one_second; } // namespace gps namespace opcodes { constexpr uint8_t no_op = 0x00; constexpr uint8_t change_downlink_window = 0x11; } // namespace opcodes - -}; // namespace constants + namespace led { + constexpr int led_pin = 9; + constexpr uint32_t led_on_time = 5 * constants::time::one_second; + } // namespace led +}; // namespace constants #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index c655380..da9097a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,23 +5,28 @@ MainControlLoop mcl; void setup() { + // Set up watchdog timer wdt_disable(); wdt_enable(WDTO_8S); #ifdef VERBOSE Serial.begin(9600); #endif - // cuts off power to GPS + sfr::gps::boot_time = millis(); + + // Set ChipSat LED high for debugging + pinMode(constants::led::led_pin, OUTPUT); + digitalWrite(constants::led::led_pin, HIGH); + + // Cut off power to GPS pinMode(constants::gps::reset_pin, OUTPUT); digitalWrite(constants::gps::reset_pin, HIGH); - // prevents current from leaking over GPS serial line (~79mA -> ~22mA) + // Prevents current from leaking over GPS serial line (~79mA -> ~22mA) pinMode(constants::gps::tx_pin, OUTPUT); digitalWrite(constants::gps::tx_pin, LOW); - sfr::gps::boot_time = millis(); - - // seed random number gen with noise from unconnected analog pin + // Seed random number gen with noise from unconnected analog pin randomSeed(analogRead(A0)); } diff --git a/src/sfr.cpp b/src/sfr.cpp index 735c711..210b713 100644 --- a/src/sfr.cpp +++ b/src/sfr.cpp @@ -1,23 +1,21 @@ #include "sfr.hpp" namespace sfr { - namespace imu { + bool initialized = false; float gyro_x, gyro_y, gyro_z; float acc_x, acc_y, acc_z; float mag_x, mag_y, mag_z; - bool initialized = false; - } // namespace imu - namespace temperature { float temp_c; } // namespace temperature namespace gps { float utc_time, latitude, longitude, altitude; - bool valid_msg = false; + bool valid_location = false; + bool valid_altitude = false; bool on = false; uint32_t boot_time = 0; @@ -27,8 +25,10 @@ namespace sfr { bool initialized = false; radio_mode_type mode = radio_mode_type::init; uint8_t start_progress = 0; + uint8_t alive_signal_dlinks = 0; - uint32_t downlink_window_length = 10 * constants::time::one_second; + // This must be a multiple of constants::radio::transmit_slot_length + uint32_t downlink_window_length = 3500; // ms uint32_t downlink_window_start; uint32_t listen_period_start; uint32_t command_wait_start; @@ -41,5 +41,4 @@ namespace sfr { uint8_t invalid_uplinks = 0; } // namespace radio - -}; // namespace sfr +}; // namespace sfr diff --git a/src/sfr.hpp b/src/sfr.hpp index 90d6910..2ad9c83 100644 --- a/src/sfr.hpp +++ b/src/sfr.hpp @@ -7,6 +7,8 @@ namespace sfr { namespace imu { + extern bool initialized; + extern float gyro_x; extern float gyro_y; extern float gyro_z; @@ -19,15 +21,15 @@ namespace sfr { extern float mag_y; extern float mag_z; - extern bool initialized; } // namespace imu namespace temperature { extern float temp_c; } // namespace temperature namespace radio { extern bool initialized; - extern uint8_t start_progress; extern radio_mode_type mode; + extern uint8_t start_progress; + extern uint8_t alive_signal_dlinks; extern uint32_t downlink_window_length; extern uint32_t downlink_window_start; @@ -49,12 +51,12 @@ namespace sfr { extern float longitude; extern float altitude; - extern bool valid_msg; + extern bool valid_location; + extern bool valid_altitude; extern bool on; extern uint32_t boot_time; } // namespace gps - -}; // namespace sfr +}; // namespace sfr #endif \ No newline at end of file