diff --git a/CSCE838-Lab4-Node/CSCE838-Lab4-Node.ino b/CSCE838-Lab4-Node/CSCE838-Lab4-Node.ino index 7735c85dd6778b689486cb8c3e39e26938738f1f..ecd81f6db0bb9e8b04376c3e43b8569744bc7c10 100644 --- a/CSCE838-Lab4-Node/CSCE838-Lab4-Node.ino +++ b/CSCE838-Lab4-Node/CSCE838-Lab4-Node.ino @@ -15,6 +15,40 @@ #include <lmic.h> #include <hal/hal.h> #include <SPI.h> +#include <TemperatureZero.h> + +/** Defines **/ +#define NODE_ID 1 +#define NETWORK_FREQUENCY 914 + +// clock parameters +#define CPU_HZ 48000000 +#define TIMER_PRESCALER_DIV 1024 + +// Error codes +#define ERROR_CODE_MISSING_PACKET 0x01 +#define ERROR_CODE_RECEPTION_FAILURE 0x02 +#define ERROR_CODE_WDT_EARLY_WARNING 0x04 +#define ERROR_CODE_INVALID_TEMPERATURE 0x08 +#define ERROR_CODE_MISMATCHED_PACKET_NUM 0x10 + +/** Type Definitions **/ +typedef struct +{ + uint8_t NodeID; + uint16_t PacketID; + uint32_t Timestamp; + float AverageTemperature; + uint8_t ErrorType; +} Lab3Packet_t; + +/** Static Variables **/ +static TemperatureZero TempZero = TemperatureZero(); +static float g_temperature_samples[5]; +static float g_temperature_samples_count = 0; +static int32_t initialMillis = 0; +static volatile Lab3Packet_t txPacket; +static volatile unsigned int counterISR = 1; // we formerly would check this configuration; but now there is a flag, // in the LMIC, LMIC.noRXIQinversion; @@ -49,24 +83,36 @@ const lmic_pinmap lmic_pins = { void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } - -void onEvent (ev_t ev) -{ - -} +void onEvent (ev_t ev) { } osjob_t txjob; osjob_t timeoutjob; static void tx_func (osjob_t* job); // Transmit the given string and call the given function afterwards +/* void tx(const char *str, osjobcb_t func) { os_radio(RADIO_RST); // Stop RX first delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet LMIC.dataLen = 0; while (*str) - LMIC.frame[LMIC.dataLen++] = *str++; + LMIC.frame[LMIC.dataLen++] = *str++; + LMIC.osjob.func = func; + os_radio(RADIO_TX); + SerialUSB.println("TX"); +} +*/ +void tx(const void* msg, uint8_t len, osjobcb_t func) +{ + os_radio(RADIO_RST); // Stop RX first + delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet + LMIC.dataLen = len; + uint16_t i; + for (i = 0; i < len; i++) + { + LMIC.frame[i] = *(((uint8_t*)msg) + i); + } LMIC.osjob.func = func; os_radio(RADIO_TX); SerialUSB.println("TX"); @@ -96,14 +142,31 @@ static void rx_func (osjob_t* job) digitalWrite(LED_BUILTIN, HIGH); // on // Timeout RX (i.e. update led status) after 3 periods without RX os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); - // Reschedule TX so that it should not collide with the other side's - // next TX + // Reschedule TX so that it should not collide with the other side's next TX os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL / 2), tx_func); - SerialUSB.print("Got "); - SerialUSB.print(LMIC.dataLen); - SerialUSB.println(" bytes"); - SerialUSB.write(LMIC.frame, LMIC.dataLen); - SerialUSB.println(); + + // Print the received output + if (LMIC.dataLen != sizeof(Lab3Packet_t)) + { + SerialUSB.println("Error: improper RX packet size."); + } + else + { + Lab3Packet_t* packet = (Lab3Packet_t*)LMIC.frame; + + if (initialMillis == 0) + { + initialMillis = millis() - packet->Timestamp; + } + printPacket(packet); + } + + //SerialUSB.print("Got "); + //SerialUSB.print(LMIC.dataLen); + //SerialUSB.println(" bytes"); + //SerialUSB.write(LMIC.frame, LMIC.dataLen); + //SerialUSB.println(); + // Restart RX rx(rx_func); } @@ -117,7 +180,22 @@ static void txdone_func (osjob_t* job) static void tx_func (osjob_t* job) { // say hello - tx("Hello, world!", txdone_func); + //tx("Hello, world!", txdone_func); + + // Construct packet + constructTxPacket((Lab3Packet_t*)&txPacket); + + // Send the packet + tx((Lab3Packet_t*)&txPacket, sizeof(txPacket), txdone_func); + + // Print the packet + printPacket((Lab3Packet_t*)&txPacket); + IsErrorDetect((Lab3Packet_t*)&txPacket); + + // Set up the next packet's packet ID + txPacket.PacketID++; + txPacket.ErrorType = 0; + // reschedule job every TX_INTERVAL (plus a bit of random to prevent systematic collisions), unless packets are received, then rx_func will reschedule at half this time. os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); } @@ -125,9 +203,17 @@ static void tx_func (osjob_t* job) // application entry point void setup() { + // Initialize temperature readings, start timer that gets temperature samples + TempZero.init(); + startTimer(1); + + // Initialize Serial USB interface SerialUSB.begin(115200); while (!SerialUSB); // wait for Serial Monitor to be attached to SparkFun Pro RF board SerialUSB.println("Starting"); + + // Initialize transmit packet + initPacket((Lab3Packet_t*)&txPacket); pinMode(LED_BUILTIN, OUTPUT); // initialize runtime env @@ -137,7 +223,7 @@ void setup() uint32_t uBandwidth; LMIC.freq = 915000000; // originally was 903900000 in the example uBandwidth = 125; - LMIC.datarate = US915_DR_SF10; // DR4, originally was US915_DR_SF7 in the example + LMIC.datarate = US915_DR_SF7; // DR4 LMIC.txpow = 20; // originally was 21 in the example // disable RX IQ inversion @@ -168,3 +254,213 @@ void loop() // execute scheduled jobs and events os_runloop_once(); } + + + +/** Function Definitions **/ +int getNewTemperatureSample() +{ + // Returns 0 if new temperature sample is valid, returns -1 if not + float temperature = TempZero.readInternalTemperature(); + SerialUSB.print("Temp = "); + SerialUSB.println(temperature); + + // Is this a valid temperature? + if ((temperature >= 0.1) && (temperature <= 100.0)) + { + // This is a valid temperature + // Shift over the sample buffer + int i; + for (i = 4; i > 0; i--) + { + g_temperature_samples[i] = g_temperature_samples[i - 1]; + } + + // Put the new temperature in the sample buffer + g_temperature_samples[0] = temperature; + + // If the sample buffer is not full, increment the number of samples in it + if (g_temperature_samples_count < 5) + { + g_temperature_samples_count++; + } + + return 0; + } + else + { + return -1; + } +} + +float getAverageTemperature() +{ + // Returns the average temperature if there are 5 valid temperature samples in the buffer, returns NAN otherwise + if (g_temperature_samples_count == 5) + { + // Calculate average + float sum = 0; + int i; + for (i = 0; i < 5; i++) + { + sum += g_temperature_samples[i]; + } + return sum / 5.0; + } + else + { + return NAN; + } +} + +void initPacket(Lab3Packet_t* packet) +{ + packet->NodeID = NODE_ID; + packet->PacketID = 1; + packet->Timestamp = 0; + packet->AverageTemperature = 0; + packet->ErrorType = 0x00; +} + +String packetToString(Lab3Packet_t* packet, bool printThisDeviceTag) +{ + String s = String("Node ID = ") + + String(packet->NodeID) + + String(", Packet ID = ") + + String(packet->PacketID) + + String(", Timestamp = ") + + String(packet->Timestamp) + + String(", Average Temperature = ") + + String(packet->AverageTemperature) + + String(", Error Type = 0x") + + String(packet->ErrorType, HEX) + ; + + if (printThisDeviceTag && (packet->NodeID == NODE_ID)) + { + s += String(" (this device)"); + } + + return s; +} + +void printPacket(Lab3Packet_t* packet) +{ + SerialUSB.println(packetToString(packet, true)); +} + +void constructTxPacket(Lab3Packet_t* packet_out) +{ + // Assumes that you have added all the errors to packet->ErrorType before calling this function + + // Get the average temperature + float averageTemperature = getAverageTemperature(); + if ((averageTemperature != averageTemperature) || (averageTemperature > 60) || (averageTemperature < 0)) + { + packet_out->ErrorType |= ERROR_CODE_INVALID_TEMPERATURE; + } + + // Populate packet with data + packet_out->Timestamp = millis() - initialMillis; + packet_out->AverageTemperature = averageTemperature; + + return; +} + +void setTimerFrequency(int frequencyHz) +{ + int compareValue3 = (CPU_HZ / (TIMER_PRESCALER_DIV * frequencyHz)) - 1; + TcCount16* TC4p = (TcCount16*) TC4; + // Make sure the count is in a proportional position to where it was + // to prevent any jitter or disconnect when changing the compare value. + TC4p->COUNT.reg = map(TC4p->COUNT.reg, 0, TC4p->CC[0].reg, 0, compareValue3); + TC4p->CC[0].reg = compareValue3; + while (TC4p->STATUS.bit.SYNCBUSY == 1); +} + +void startTimer(int frequencyHz) +{ + REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | + GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TC4_TC5) ; + while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync + TcCount16* TC4p = (TcCount16*) TC4; + TC4p->CTRLA.reg &= ~TC_CTRLA_ENABLE; //Disable timer + TC4p->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV1024; + while (TC4p->STATUS.bit.SYNCBUSY == 1); // wait for sync + setTimerFrequency(1); + TC4p->INTENSET.reg = 0; + TC4p->INTENSET.bit.OVF = 1; + NVIC_EnableIRQ(TC4_IRQn); + TC4p->CTRLA.reg |= TC_CTRLA_ENABLE; + while (TC4p->STATUS.bit.SYNCBUSY == 1); // wait for sync +} + +bool IsErrorDetect(Lab3Packet_t* packet) +{ + if (packet->ErrorType == 0) + { + return false; + } + + uint32_t errorCode = 0 + | ((uint32_t)packet->ErrorType) << 24 // ErrorType occupies bits 31 downto 24 + | ((uint32_t)packet->NodeID) << 16 // NodeID occupies bits 23 downto 16 + | ((uint32_t)packet->PacketID) // PacketID occupies bits 15 downto 0 + ; + + // Print an error message + printError(errorCode); + + return true; +} + +void printError(uint32_t errorCode) +{ + uint8_t errorType = errorCode >> 24; + uint8_t nodeID = errorCode >> 16; + uint16_t packetID = errorCode; + + SerialUSB.print("Error(s) on Node ID "); + SerialUSB.print(": "); + + if (errorType == 0) + { + SerialUSB.println("No errors"); + return; + } + + SerialUSB.print("Error(s): "); + if (errorType & ERROR_CODE_MISSING_PACKET) + { + SerialUSB.print("{Missing packet} "); + } + if (errorType & ERROR_CODE_RECEPTION_FAILURE) + { + SerialUSB.print("{Reception failure} "); + } + if (errorType & ERROR_CODE_WDT_EARLY_WARNING) + { + SerialUSB.print("{WDT early warning} "); + } + if (errorType & ERROR_CODE_INVALID_TEMPERATURE) + { + SerialUSB.print("{Invalid temperature} "); + } + SerialUSB.println(""); +} + +/** ISRs **/ +void TC4_Handler() +{ + TcCount16* TC4p = (TcCount16*) TC4; + // If this interrupt is due to the compare register matching the timer count + // we toggle the LED. + if (TC4p->INTFLAG.bit.OVF == 1) + { + TC4p->INTFLAG.bit.OVF = 1; + // Write callback here!!! + // This handler executes every 1.0 seconds to measure the temperature and add it to the moving average + getNewTemperatureSample(); + // five second block counter + } +} \ No newline at end of file