From 04a307126ab411821148ae61556ed6c989dbe7a9 Mon Sep 17 00:00:00 2001
From: rrodoshi2 <rrodoshi2@huskers.unl.edu>
Date: Thu, 22 Sep 2022 13:12:23 -0500
Subject: [PATCH] Changed LMIC frequency to 903.3 MHz

---
 CSCE838-Lab4-Node.ino | 466 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 466 insertions(+)
 create mode 100644 CSCE838-Lab4-Node.ino

diff --git a/CSCE838-Lab4-Node.ino b/CSCE838-Lab4-Node.ino
new file mode 100644
index 0000000..85b054b
--- /dev/null
+++ b/CSCE838-Lab4-Node.ino
@@ -0,0 +1,466 @@
+/*******************************************************************************
+  Copyright (c) 2015 Matthijs Kooijman
+  Copyright (c) 2018 Terry Moore, MCCI Corporation
+
+  Permission is hereby granted, free of charge, to anyone
+  obtaining a copy of this document and accompanying files,
+  to do whatever they want with them without any restriction,
+  including, but not limited to, copying, modification and redistribution.
+  NO WARRANTY OF ANY KIND IS PROVIDED.
+
+  This example transmits data on hardcoded channel and receives data
+  when not transmitting. Running this sketch on two nodes should allow
+  them to communicate.
+*******************************************************************************/
+#include <lmic.h>
+#include <hal/hal.h>
+#include <SPI.h>
+#include <TemperatureZero.h>
+
+/** Defines **/
+#define NODE_ID  1
+#define NETWORK_FREQUENCY 915
+
+// 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;
+// if we set that during init, we get the same effect. If
+// DISABLE_INVERT_IQ_ON_RX is defined, it means that LMIC.noRXIQinversion is
+// treated as always set.
+//
+// #if !defined(DISABLE_INVERT_IQ_ON_RX)
+// #error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \
+// lmic_project_config.h in arduino-lmic/project_config to set it.
+// #endif
+// How often to send a packet. Note that this sketch bypasses the normal
+// LMIC duty cycle limiting, so when you change anything in this sketch
+// (payload length, frequency, spreading factor), be sure to check if
+// this interval should not also be increased.
+// See this spreadsheet for an easy airtime and duty cycle calculator:
+// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc
+#define TX_INTERVAL 60000 //Delay between each message in millisecond.
+
+// Pin mapping for SAMD21
+const lmic_pinmap lmic_pins = {
+  .nss = 12,//RFM Chip Select
+  .rxtx = LMIC_UNUSED_PIN,
+  .rst = 7,//RFM Reset
+  .dio = {6, 10, 11}, //RFM Interrupt, RFM LoRa pin, RFM LoRa pin
+};
+
+// These callbacks are only used in over-the-air activation, so they are
+// left empty here (we cannot leave them out completely unless
+// DISABLE_JOIN is set in arduino-lmoc/project_config/lmic_project_config.h,
+// otherwise the linker will complain).
+void os_getArtEui (u1_t* buf) { }
+void os_getDevEui (u1_t* buf) { }
+void os_getDevKey (u1_t* buf) { }
+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.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");
+}
+
+// Enable rx mode and call func when a packet is received
+void rx(osjobcb_t func)
+{
+  LMIC.osjob.func = func;
+  LMIC.rxtime = os_getTime(); // RX _now_
+  // Enable "continuous" RX (e.g. without a timeout, still stops after
+  // receiving a packet)
+  os_radio(RADIO_RXON);
+  SerialUSB.println("RX");
+}
+
+static void rxtimeout_func(osjob_t *job)
+{
+  digitalWrite(LED_BUILTIN, LOW); // off
+}
+
+static void rx_func (osjob_t* job)
+{
+  // Blink once to confirm reception and then keep the led on
+  digitalWrite(LED_BUILTIN, LOW); // off
+  delay(10);
+  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
+  os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL / 2), tx_func);
+
+  // 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);
+}
+
+static void txdone_func (osjob_t* job)
+{
+  //rx(rx_func);
+}
+
+// log text to USART and toggle LED
+static void tx_func (osjob_t* job)
+{
+  // say hello
+  tx("Hello, world!", 14, txdone_func);
+  SerialUSB.println("Hello world");
+  
+  // 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);
+}
+
+// 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
+  os_init();
+  
+  // this is automatically set to the proper bandwidth in kHz, based on the selected channel
+  uint32_t uBandwidth;
+  //LMIC.freq = 000000; // originally was 903900000 in the example
+  LMIC.freq = 903900000;
+  uBandwidth = NETWORK_FREQUENCY * 1000000;
+  LMIC.datarate = US915_DR_SF9; // DR4 // originally US915_DR_SF7
+  LMIC.txpow = 20;  // originally was 21 in the example
+  
+  // disable RX IQ inversion
+  LMIC.noRXIQinversion = true;
+  
+  // This sets CR 4/5, BW125 (except for EU/AS923 DR_SF7B, which uses BW250)
+  LMIC.rps = updr2rps(LMIC.datarate);
+  SerialUSB.print("Frequency: "); SerialUSB.print(LMIC.freq / 1000000);
+  SerialUSB.print("."); SerialUSB.print((LMIC.freq / 100000) % 10);
+  SerialUSB.print("MHz");
+  SerialUSB.print(" LMIC.datarate: "); SerialUSB.print(LMIC.datarate);
+  SerialUSB.print(" LMIC.txpow: "); SerialUSB.println(LMIC.txpow);
+  
+  // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250)
+  LMIC.rps = updr2rps(LMIC.datarate);
+  
+  // disable RX IQ inversion
+  LMIC.noRXIQinversion = true;
+  SerialUSB.println("Started");
+  SerialUSB.flush();
+  
+  // setup initial job
+  os_setCallback(&txjob, tx_func);
+}
+
+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();
+  
+  // 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
+  }
+}
-- 
GitLab