/******************************************************************************* * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman * * 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 sends a valid LoRaWAN packet with payload "Hello, * world!", using frequency and encryption settings matching those of * the The Things Network. * * This uses OTAA (Over-the-air activation), where where a DevEUI and * application key is configured, which are used in an over-the-air * activation procedure where a DevAddr and session keys are * assigned/generated for use with all further communication. * * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably * violated by this sketch when left running for longer)! * To use this sketch, first register your application and device with * the things network, to set or generate an AppEUI, DevEUI and AppKey. * Multiple devices can use the same AppEUI, but each device has its own * DevEUI and AppKey. * * Do not forget to define the radio type correctly in config.h. * *******************************************************************************/ #include #include #include #include #include #include #include #include "config.h" void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} static uint8_t mydata[] = "ping"; static osjob_t sendjob; // Schedule TX every this many seconds (might become longer due to duty // cycle limitations). const unsigned TX_INTERVAL = 30; const lmic_pinmap lmic_pins = { .nss = 10, .rxtx = LMIC_UNUSED_PIN, .rst = LMIC_UNUSED_PIN, // hardwired to AtMega RESET .dio = {4,5,LMIC_UNUSED_PIN}// .dio = {4, 5, LMIC_UNUSED_PIN}, }; CayenneLPP lpp(51); BME280I2C bme; long readVcc() { // Read 1.1V reference against AVcc // set the reference to Vcc and the measurement to the internal 1.1V reference #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = _BV(MUX3) | _BV(MUX2); #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Start conversion while (bit_is_set(ADCSRA,ADSC)); // measuring uint8_t low = ADCL; // must read ADCL first - it then locks ADCH uint8_t high = ADCH; // unlocks both long result = (high<<8) | low; result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 return result; // Vcc in millivolts } 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_BEACON_FOUND: Serial.println(F("EV_BEACON_FOUND")); break; case EV_BEACON_MISSED: Serial.println(F("EV_BEACON_MISSED")); break; case EV_BEACON_TRACKED: Serial.println(F("EV_BEACON_TRACKED")); break; case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); // Disable link check validation (automatically enabled // during join, but not supported by TTN at this time). LMIC_setLinkCheckMode(0); break; case EV_RFU1: Serial.println(F("EV_RFU1")); break; case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); break; break; case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { Serial.println(F("Received ")); Serial.println(LMIC.dataLen); Serial.println(F(" bytes of payload")); } /*Serial.print(F("Frequency: ")); Serial.println(LMIC.freq); Serial.print(F("RSSI: ")); Serial.println(LMIC.rssi); Serial.print(F("SNR: ")); Serial.println(LMIC.snr); Serial.print(F("txpow: ")); Serial.println(LMIC.txpow);*/ Serial.print(F("adrTxPow: ")); Serial.println(LMIC.adrTxPow); Serial.print(F("txChnl: ")); Serial.println(LMIC.txChnl); Serial.println(); // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); 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; default: Serial.println(F("Unknown event")); break; } } 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 { float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::PresUnit presUnit(BME280::PresUnit_hPa); bme.read(pres, temp, hum, tempUnit, presUnit); float voltage = readVcc() / 1000.0 ; lpp.reset(); lpp.addTemperature(1, temp); lpp.addRelativeHumidity(2, hum); lpp.addBarometricPressure(3, pres); lpp.addAnalogInput(4, voltage); // Prepare upstream data transmission at the next possible time. LMIC_setTxData2(1, lpp.getBuffer(), lpp.getSize(), 0); Serial.println(F("Packet queued")); } // Next TX is scheduled after TX_COMPLETE event. } void setup() { Serial.begin(9600); Serial.println(F("Starting TTN Muc Cayenne Sensor 1")); while(!bme.begin()) { Serial.println("Could not find BME280 sensor!"); delay(1000); } // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); // Let LMIC compensate for +/- 1% clock error LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); // Start job (sending automatically starts OTAA too) do_send(&sendjob); } void loop() { os_runloop_once(); }