#include "myHeader.h" void onEvent (ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch(ev) { case EV_SCAN_TIMEOUT: Serial.println(F("EV_SCAN_TIMEOUT")); break; case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); break; // Has to be tested case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); goToSleep(time_between_join_requests); do_send(&sendjob); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); break; case EV_TXCOMPLETE: Serial.println(F("TX_COMPLETE")); sleep_flag = true; // The whole attach/detach thing may be not needed attachInterrupt(digitalPinToInterrupt(CONTACT_SENSOR_PIN), interrupt_RISING, RISING); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); break; case EV_RESET: Serial.println(F("EV_RESET")); break; case EV_RXCOMPLETE: // data received in ping slot Serial.println(F("EV_RXCOMPLETE")); break; case EV_LINK_DEAD: Serial.println(F("EV_LINK_DEAD")); break; case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; case EV_TXSTART: Serial.println(F("EV_TXSTART")); break; default: Serial.print(F("Unknown event: ")); Serial.println((unsigned) ev); break; } } void goToSleep (int sleep_time) { // The deatch/attach USB device routine is only useful to read the serial after the Feather wakes up from the deep sleep USBDevice.detach(); rtc_sleep.setEpoch(0; long unsigned int curr_time = rtc_sleep.getEpoch(); long unsigned int wake_up_time = curr_time + sleep_time; long unsigned int sleep_time_left = sleep_time; // this while statement is needed to resume a sleep cycle when it is stopped by an interrupt while (curr_time < wake_up_time) { // [0, .. < 60] sleep_time_left = wake_up_time - curr_time; // [60 - 0, ..] if (sleep_time_left > 0) { LowPower.deepSleep(sleep_time_left * 1000); } else { wake_up_time = 0; } curr_time = rtc_sleep.getEpoch(); } USBDevice.attach(); } // Alternative to the do_send function, essentially the same but without the osjob_t parameter void start_TX () { if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND, not sending")); } else { DHT_reading(); door_state = digitalRead(CONTACT_SENSOR_PIN); battery_reading(); BMP_reading(); preparaPayload(); door_openings = 0; LMIC_setTxData2(1, payload, sizeof(payload), 0); } // Next TX is scheduled after TX_COMPLETE event. } void do_send(osjob_t* j){ // Check if there is not a current TX/RX job running if (LMIC.opmode & OP_TXRXPEND) { Serial.println(F("OP_TXRXPEND, not sending")); } else { DHT_reading(); door_state = digitalRead(CONTACT_SENSOR_PIN); battery_reading(); BMP_reading(); preparaPayload(); door_openings = 0; LMIC_setTxData2(1, payload, sizeof(payload), 0); } // Next TX is scheduled after TX_COMPLETE event. } void interrupt_RISING () { // Variable needed to avoid "false positives" for the openings int test = digitalRead(CONTACT_SENSOR_PIN); if (test and door_openings <= 255) { door_openings++; } } void DHT_reading() { DHT_temperature = dht.readTemperature(); humidity = dht.readHumidity(); DHT_temp_reading_failed = isnan(DHT_temperature); DHT_hum_reading_failed = isnan(humidity); } void battery_reading(){ battery_tension = analogRead(BATTERY_PIN) * 2; battery_tension *= 3.3; battery_tension /= 1024; } void BMP_reading() { if (!bmp.performReading()) { BMP_reading_failed = true; return; } BMP_reading_failed = false; BMP_temperature = bmp.temperature; pressure = bmp.pressure / 100.0; } void preparaPayload () { if (!BMP_reading_failed && !DHT_temp_reading_failed) { temperature = (BMP_temperature + DHT_temperature) / 2; } else if (BMP_reading_failed && !DHT_temp_reading_failed) { temperature = DHT_temperature; } else if (!BMP_reading_failed && DHT_temp_reading_failed) { temperature = BMP_temperature; } else { // Special value for the failed reading to be handled in the decoding phase temperature = -40.0; } if (isnan(humidity)) { // Special value for the failed reading to be handled in the decoding phase humidity = 500.0; } if (BMP_reading_failed) { // Special value for the failed reading to be handled in the decoding phase pressure = 500.00; } // Reasonable range for the pressure value else if (pressure >= 800.0 and pressure <= 1200.0) { // Shift the range of the pressure value in order to fit it into a 16 bit integer pressure -= 800.0; } // Special value for the failed reading to be handled in the decoding phase else { pressure = 600.00; // Uso un valore specifico ad indicare l'errore } int16_t temp_payload = temperature * 100; uint16_t hum_payload = humidity * 100; uint16_t press_payload = pressure * 100; uint16_t batt_payload = battery_tension * 100; uint8_t door_state_payload = door_state; uint8_t door_openings_payload = door_openings; payload[0] = temp_payload >> 8; payload[1] = temp_payload; payload[2] = hum_payload >> 8; payload[3] = hum_payload; payload[4] = batt_payload >> 8; payload[5] = batt_payload; payload[6] = press_payload >> 8; payload[7] = press_payload; payload[8] = door_state_payload; payload[9] = door_openings_payload; } void enableEdgeInterrupts () { attachInterrupt(digitalPinToInterrupt(CONTACT_SENSOR_PIN), interrupt_RISING, RISING); // Set the XOSC32K to run in standby SYSCTRL->XOSC32K.bit.RUNSTDBY = 1; // Configure EIC to use GCLK1 which uses XOSC32K // This has to be done after the first call to attachInterrupt() GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_EIC) | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_CLKEN; detachInterrupt(digitalPinToInterrupt(CONTACT_SENSOR_PIN)); } void setup() { pinMode(LED_BUILTIN, OUTPUT); pinMode(DHT_PIN,INPUT); pinMode(CONTACT_SENSOR_PIN,INPUT_PULLUP); pinMode(BATTERY_PIN, INPUT); digitalWrite(LED_BUILTIN,LOW); delay(3000); rtc_sleep.begin(); rtc_sleep.setEpoch(0); enableEdgeInterrupts(); attachInterrupt(digitalPinToInterrupt(CONTACT_SENSOR_PIN), interrupt_RISING, RISING); dht.begin(); bmp.begin(); // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); // Disable link-check mode and ADR, because ADR tends to complicate testing. LMIC_setLinkCheckMode(0); // Set the data rate to Spreading Factor 7. This is the fastest supported rate for 125 kHz channels, and it // minimizes air time and battery power. Set the transmission power to 14 dBi (25 mW). LMIC_setDrTxpow(DR_SF7,14); // in the US, with TTN, it saves join time if we start on subband 1 (channels 8-15). This will // get overridden after the join by parameters from the network. If working with other // networks or in other regions, this will need to be changed. //LMIC_selectSubBand(1); LMIC_setClockError(MAX_CLOCK_ERROR * 2 / 100); // Start job (sending automatically starts OTAA too) // +++ TO DO - understand if there's a difference between these two functions +++ //do_send(&sendjob); start_TX(); } void loop() { debug(); bool send_flag = false; os_runloop_once(); // If enough sleep cycles have been completed and the TX interval has been exceeded if (sleep_cycles_done * sleep_subperiod >= TX_interval) { send_flag = true; } if (send_flag) { detachInterrupt(digitalPinToInterrupt(CONTACT_SENSOR_PIN)); send_flag = false; sleep_flag = false; sleep_cycles_done = 0; // +++ TO DO - understand if there's a difference between these two functions +++ //do_send(&sendjob); start_TX(); } if (sleep_flag) { //delay(sleep_subperiod*1000); // DEBUG goToSleep(sleep_subperiod); sleep_cycles_done++; } }