Test LORA NODE with Library SX1272, initially created by C.Pham, University of Pau, France for Arduino. Suitable for MBED / NUCLEO / STM32

Dependencies:   SX1272 mbed

This project is an adaptation of C.Pham project, university of Pau, France. Initially developed for Arduino, the library sx1272 and the test program was focused on NUCLEO STM32 by C.Dupaty, high school Fourcade 13120 Gardanne.

Tested on NUCLEO L073 with

- SX1272MB2xAS SHIELD

/media/uploads/cdupaty/picture3.png.200x200_q85.png

- DRAGINO SHIELD V95 WITH GPS http://wiki.dragino.com/index.php?title=Lora/GPS_Shield For DRAGINO move LORA_CLK LORA_DI LORA_DO straps to the right (arduino 11 12 13)

/media/uploads/cdupaty/lora_gps_shield_1.jpg

ALL CONFIGURATIONS FOR ARDUINO HAVE BEEN REMOVED WORK ONLY IN EUROPE

please visit http://cpham.perso.univ-pau.fr/LORA/LoRaDevices.html for original version for ARDUINO

main.cpp

Committer:
cdupaty
Date:
2017-11-05
Revision:
0:9859cc8476f2
Child:
1:9f961d34dd8d

File content as of revision 0:9859cc8476f2:

/*
 *  temperature sensor on analog 8 to test the LoRa gateway
 *
 *  Copyright (C) 2016 Congduc Pham, University of Pau, France
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.

 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with the program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *****************************************************************************
 * last update: Sep. 29th, 2017 by C. Pham
 * last update: oct 30th , 2017 by C.Dupaty
 */
#include "mbed.h"

#include <SPI.h> 
// Include the SX1272
#include "SX1272.h"

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// please uncomment only 1 choice
//
#define ETSI_EUROPE_REGULATION
//#define FCC_US_REGULATION
//#define SENEGAL_REGULATION
/////////////////////////////////////////////////////////////////////////////////////////////////////////// 

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// please uncomment only 1 choice
#define BAND868
//#define BAND900
//#define BAND433
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ETSI_EUROPE_REGULATION
#define MAX_DBM 14
// previous way for setting output power
// char powerLevel='M';
#elif defined SENEGAL_REGULATION
#define MAX_DBM 10
// previous way for setting output power
// 'H' is actually 6dBm, so better to use the new way to set output power
// char powerLevel='H';
#elif defined FCC_US_REGULATION
#define MAX_DBM 14
#endif

#ifdef BAND868
#ifdef SENEGAL_REGULATION
const uint32_t DEFAULT_CHANNEL=CH_04_868;
#else
const uint32_t DEFAULT_CHANNEL=CH_10_868;
#endif
#elif defined BAND900
const uint32_t DEFAULT_CHANNEL=CH_05_900;
#elif defined BAND433
const uint32_t DEFAULT_CHANNEL=CH_00_433;
#endif

// IMPORTANT
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// uncomment if your radio is an HopeRF RFM92W, HopeRF RFM95W, Modtronix inAir9B, NiceRF1276
// or you known from the circuit diagram that output use the PABOOST line instead of the RFO line
//#define PABOOST
/////////////////////////////////////////////////////////////////////////////////////////////////////////// 

///////////////////////////////////////////////////////////////////
// COMMENT OR UNCOMMENT TO CHANGE FEATURES. 
// ONLY IF YOU KNOW WHAT YOU ARE DOING!!! OTHERWISE LEAVE AS IT IS
//#define WITH_EEPROM             //  tous uncomment a l origine sauf WITH_ACK
#define WITH_APPKEY
#define FLOAT_TEMP
#define NEW_DATA_FIELD
//#define LOW_POWER
//#define LOW_POWER_HIBERNATE
//#define WITH_ACK
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// ADD HERE OTHER PLATFORMS THAT DO NOT SUPPORT EEPROM NOR LOW POWER
#if defined ARDUINO_SAM_DUE || defined __SAMD21G18A__ || defined TARGET_NUCLEO_L073RZ
#undef WITH_EEPROM
#endif

#if defined ARDUINO_SAM_DUE || defined TARGET_NUCLEO_L073RZ
#undef LOW_POWER
#endif
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// CHANGE HERE THE LORA MODE, NODE ADDRESS 
#define LORAMODE  1
#define node_addr 6
//////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// CHANGE HERE THE THINGSPEAK FIELD BETWEEN 1 AND 4
#define field_index 1
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// CHANGE HERE THE READ PIN AND THE POWER PIN FOR THE TEMP. SENSOR
#define TEMP_PIN_READ  PA_0
// use digital 9 to power the temperature sensor if needed
#define TEMP_PIN_POWER PA_1
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
// CHANGE HERE THE TIME IN MINUTES BETWEEN 2 READING & TRANSMISSION
unsigned int idlePeriodInMin = 2;
///////////////////////////////////////////////////////////////////

#ifdef WITH_APPKEY
///////////////////////////////////////////////////////////////////
// CHANGE HERE THE APPKEY, BUT IF GW CHECKS FOR APPKEY, MUST BE
// IN THE APPKEY LIST MAINTAINED BY GW.
uint8_t my_appKey[4]={5, 6, 7, 8};
///////////////////////////////////////////////////////////////////
#endif

// we wrapped Serial.println to support the Arduino Zero or M0
#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0
#define PRINTLN                   SerialUSB.println("")              
#define PRINT_CSTSTR(fmt,param)   SerialUSB.print(F(param))
#define PRINT_STR(fmt,param)      SerialUSB.print(param)
#define PRINT_VALUE(fmt,param)    SerialUSB.print(param)
#define FLUSHOUTPUT               SerialUSB.flush();
#else
#define PRINTLN                   Serial.println("")
#define PRINT_CSTSTR(fmt,param)   Serial.print(F(param))
#define PRINT_STR(fmt,param)      Serial.print(param)
#define PRINT_VALUE(fmt,param)    Serial.print(param)
#define FLUSHOUTPUT               Serial.flush();
#endif

#ifdef WITH_EEPROM
#include <EEPROM.h>
#endif

#define DEFAULT_DEST_ADDR 1

#ifdef WITH_ACK
#define NB_RETRIES 2
#endif

#if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_MINI || defined ARDUINO_SAM_DUE || defined __MK20DX256__  || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__ || defined __SAMD21G18A__
  // if you have a Pro Mini running at 5V, then change here
  // these boards work in 3.3V
  // Nexus board from Ideetron is a Mini
  // __MK66FX1M0__ is for Teensy36
  // __MK64FX512__  is for Teensy35
  // __MK20DX256__ is for Teensy31/32
  // __MKL26Z64__ is for TeensyLC
  // __SAMD21G18A__ is for Zero/M0 and FeatherM0 (Cortex-M0)
  #define TEMP_SCALE  3300.0
#else // ARDUINO_AVR_NANO || defined ARDUINO_AVR_UNO || defined ARDUINO_AVR_MEGA2560 
  // also for all other boards, so change here if required.
  #define TEMP_SCALE  5000.0
#endif

#ifdef LOW_POWER
// this is for the Teensy36, Teensy35, Teensy31/32 & TeensyLC
// need v6 of Snooze library
#if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__
#define LOW_POWER_PERIOD 60
#include <Snooze.h>
SnoozeTimer timer;
SnoozeBlock sleep_config(timer);
//#elif defined ARDUINO_AVR_FEATHER32U4
//#define LOW_POWER_PERIOD 8
//#include "Adafruit_SleepyDog.h"
#else // for all other boards based on ATMega168, ATMega328P, ATMega32U4, ATMega2560, ATMega256RFR2, ATSAMD21G18A
#define LOW_POWER_PERIOD 8
// you need the LowPower library from RocketScream
// https://github.com/rocketscream/Low-Power
#include "LowPower.h"

#ifdef __SAMD21G18A__
// use the RTC library
#include "RTCZero.h"
/* Create an rtc object */
RTCZero rtc;
#endif
#endif
unsigned int nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD;
#endif

double temp;
unsigned long nextTransmissionTime=0L;
char float_str[20];
uint8_t message[100];
int loraMode=LORAMODE;

#ifdef WITH_EEPROM
struct sx1272config {

  uint8_t flag1;
  uint8_t flag2;
  uint8_t seq;
  // can add other fields such as LoRa mode,...
};

sx1272config my_sx1272config;
#endif

// ajoute par C.Dupaty
//Serial pcmain(USBTX, USBRX); // tx, rx
DigitalOut temp_pin_power(TEMP_PIN_POWER);
AnalogIn temp_pin_read(TEMP_PIN_READ);

void setup()
{
  int e;

  // for the temperature sensor
  //pinMode(TEMP_PIN_READ, INPUT);
  // and to power the temperature sensor
  //pinMode(TEMP_PIN_POWER,OUTPUT);

#ifdef LOW_POWER
#ifdef __SAMD21G18A__
  rtc.begin();
#endif  
#else
 // digitalWrite(TEMP_PIN_POWER,HIGH);
 temp_pin_power=1;
#endif

  wait_ms(3000);
/*
  // Open serial communications and wait for port to open:
#if defined __SAMD21G18A__ && not defined ARDUINO_SAMD_FEATHER_M0 
  SerialUSB.begin(38400);
#else
 Serial.begin(38400);  
#endif 
*/
 
  // Print a start message
  printf("%s","Simple LoRa temperature sensor\n");
  printf("%s","--------------------------------\n");
  printf("%s","---- VERSION NUCLEO STM32-------\n");
  printf("%s","--------------------------------\n");
#ifdef ARDUINO_AVR_PRO
  printf("%s","Arduino Pro Mini detected\n");  
#endif
#ifdef ARDUINO_AVR_NANO
  printf("%s","Arduino Nano detected\n");   
#endif
#ifdef ARDUINO_AVR_MINI
  printf("%s","Arduino Mini/Nexus detected\n");  
#endif
#ifdef ARDUINO_AVR_MEGA2560
  printf("%s","Arduino Mega2560 detected\n");  
#endif
#ifdef ARDUINO_SAM_DUE
  printf("%s","Arduino Due detected\n");  
#endif
#ifdef __MK66FX1M0__
  printf("%s","Teensy36 MK66FX1M0 detected\n");
#endif
#ifdef __MK64FX512__
  printf("%s","Teensy35 MK64FX512 detected\n");
#endif
#ifdef __MK20DX256__
  printf("%s","Teensy31/32 MK20DX256 detected\n");
#endif
#ifdef __MKL26Z64__
  printf("%s","TeensyLC MKL26Z64 detected\n");
#endif
#ifdef ARDUINO_SAMD_ZERO 
  printf("%s","Arduino M0/Zero detected\n");
#endif
#ifdef ARDUINO_AVR_FEATHER32U4 
  printf("%s","Adafruit Feather32U4 detected\n"); 
#endif
#ifdef ARDUINO_SAMD_FEATHER_M0
  printf("%s","Adafruit FeatherM0 detected\n");
#endif

// See http://www.nongnu.org/avr-libc/user-manual/using_tools.html
// for the list of define from the AVR compiler

#ifdef __AVR_ATmega328P__
  printf("%s","ATmega328P detected\n");
#endif 
#ifdef __AVR_ATmega32U4__
  printf("%s","ATmega32U4 detected\n");
#endif 
#ifdef __AVR_ATmega2560__
  printf("%s","ATmega2560 detected\n");
#endif 
#ifdef __SAMD21G18A__ 
  printf("%s","SAMD21G18A ARM Cortex-M0 detected\n");
#endif
#ifdef __SAM3X8E__ 
  printf("%s","SAM3X8E ARM Cortex-M3 detected\n");
#endif

#ifdef TARGET_NUCLEO_L073RZ
  printf("%s","NUCLEO L073RZ detected\n");
#endif

  // Power ON the module
  sx1272.ON();

#ifdef WITH_EEPROM
  // get config from EEPROM
  EEPROM.get(0, my_sx1272config);

  // found a valid config?
  if (my_sx1272config.flag1==0x12 && my_sx1272config.flag2==0x34) {
    printf("%s","Get back previous sx1272 config\n");

    // set sequence number for SX1272 library
    sx1272._packetNumber=my_sx1272config.seq;
    printf("%s","Using packet sequence number of ");
    printf("%d", sx1272._packetNumber);
    printf("\n");
  }
  else {
    // otherwise, write config and start over
    my_sx1272config.flag1=0x12;
    my_sx1272config.flag2=0x34;
    my_sx1272config.seq=sx1272._packetNumber;
  }
#endif
  
  int error_config_sx1272=0;
  
  // Set transmission mode and print the result
  printf("%s","\n-------------------------DEBUT Setting mode -----------> \n");
  e = sx1272.setMode(loraMode);
  printf("%s","\n-------------------------FIN Setting mode -----------> \n");
  if (e) error_config_sx1272=1;
  printf("%s","Setting Mode: state ");
  printf("%d", e);
  printf("\n");

  // enable carrier sense
  sx1272._enableCarrierSense=true;
#ifdef LOW_POWER
  // TODO: with low power, when setting the radio module in sleep mode
  // there seem to be some issue with RSSI reading
  sx1272._RSSIonSend=false;
#endif   
    
  // Select frequency channel
  e = sx1272.setChannel(DEFAULT_CHANNEL);
  if (e) error_config_sx1272=1;
  printf("%s","Setting Channel: state ");
  printf("%d", e);
  printf("\n");
  
  // Select amplifier line; PABOOST or RFO
#ifdef PABOOST
  sx1272._needPABOOST=true;
  // previous way for setting output power
  // powerLevel='x';
#else
  // previous way for setting output power
  // powerLevel='M';  
#endif

  // previous way for setting output power
  // e = sx1272.setPower(powerLevel); 

  e = sx1272.setPowerDBM((uint8_t)MAX_DBM);
  if (e) error_config_sx1272=1;
  printf("%s","Setting Power: state ");
  printf("%d", e);
  printf("\n");
  
  // Set the node address and print the result
  e = sx1272.setNodeAddress(node_addr);
  if (e) error_config_sx1272=1;
  printf("%s","Setting node addr: state ");
  printf("%d", e);
  printf("\n");
  
  // Print a success message
  if (!error_config_sx1272) printf("%s","SX1272 successfully configured\n");
  else printf("%s","ERREUR DE CONFIGURATION DU SX1272\n");

  wait_ms(500);
}

char *ftoa(char *a, double f, int precision)
{
 long p[] = {0,10,100,1000,10000,100000,1000000,10000000,100000000};
 
 char *ret = a;
 long heiltal = (long)f;
 //modifie pa C.Dupaty
// itoa(heiltal, a, 10);
sprintf(a,"%d",heiltal);
 while (*a != '\0') a++;
 *a++ = '.';
 long desimal = abs((long)((f - heiltal) * p[precision]));
 if (desimal < p[precision-1]) {
  *a++ = '0';
 } 
 
  //modifie pa C.Dupaty
//  itoa(desimal, a, 10);
sprintf(a,"%d",desimal);


 return ret;
}


//////////////////////////////////////////////////////////////
//  programme principal , loop sur Arduino
//////////////////////////////////////////////////////////////
int main(void)
{
  long startSend;
  long endSend;
  uint8_t app_key_offset=0;
  int e;
  
  setup();
  
while(1)  { /////////////// debut boucle
//printf("%s","\n-------------------------BOUCLE SANS FIN -----------> \n");
//while(1); 

#ifndef LOW_POWER
  // 600000+random(15,60)*1000
  if (millis() > nextTransmissionTime) {
#endif

#ifdef LOW_POWER
    //  digitalWrite(TEMP_PIN_POWER,HIGH);
    temp_pin_power=1;
      // security?
      wait_ms(200);   
#endif

      temp = 0;
      int value;
      
      for (int i=0; i<10; i++) {
          // change here how the temperature should be computed depending on your sensor type
          // 
           value = temp_pin_read;
          temp += (value*TEMP_SCALE/1024.0)/10;
        
          printf("%s","Reading ");
          printf("%d", value);
          printf("\n");   
          wait_ms(100);
      }
      
#ifdef LOW_POWER
      //digitalWrite(TEMP_PIN_POWER,LOW);
      temp_pin_ower=0;
#endif

      printf("%s","Mean temp is ");
      temp = temp/10;
      printf("%f", temp);
      printf("\n");
      
#ifdef WITH_APPKEY
      app_key_offset = sizeof(my_appKey);
      // set the app key in the payload
      memcpy(message,my_appKey,app_key_offset);
#endif

      uint8_t r_size;
      
#ifdef FLOAT_TEMP
      ftoa(float_str,temp,2);

#ifdef NEW_DATA_FIELD
      // this is for testing, uncomment if you just want to test, without a real temp sensor plugged
      //strcpy(float_str, "21.55567");
      r_size=sprintf((char*)message+app_key_offset,"\\!#%d#TC/%s",field_index,float_str);
#else
      // this is for testing, uncomment if you just want to test, without a real temp sensor plugged
      //strcpy(float_str, "21.55567");
      r_size=sprintf((char*)message+app_key_offset,"\\!#%d#%s",field_index,float_str);
#endif
      
#else
      
#ifdef NEW_DATA_FIELD      
      r_size=sprintf((char*)message+app_key_offset, "\\!#%d#TC/%d", field_index, (int)temp);   
#else
      r_size=sprintf((char*)message+app_key_offset, "\\!#%d#%d", field_index, (int)temp);
#endif         
#endif

      printf("%s","Sending ");
      printf("%s",(char*)(message+app_key_offset));
      printf("\n");
      
      printf("%s","Real payload size is ");
      printf("%d", r_size);
      printf("\n");
      
      int pl=r_size+app_key_offset;
      
      sx1272.CarrierSense();
      
      startSend=millis();

#ifdef WITH_APPKEY
      // indicate that we have an appkey
      sx1272.setPacketType(PKT_TYPE_DATA | PKT_FLAG_DATA_WAPPKEY);
#else
      // just a simple data packet
      sx1272.setPacketType(PKT_TYPE_DATA);
#endif
      
      // Send message to the gateway and print the result
      // with the app key if this feature is enabled
#ifdef WITH_ACK
      int n_retry=NB_RETRIES;
      
      do {
        e = sx1272.sendPacketTimeoutACK(DEFAULT_DEST_ADDR, message, pl);

        if (e==3)
          printf("%s","No ACK");
        
        n_retry--;
        
        if (n_retry)
          printf("%s","Retry");
        else
          printf("%s","Abort"); 
          
      } while (e && n_retry);          
#else      
      e = sx1272.sendPacketTimeout(DEFAULT_DEST_ADDR, message, pl);
#endif  
      endSend=millis();
    
#ifdef WITH_EEPROM
      // save packet number for next packet in case of reboot
      my_sx1272config.seq=sx1272._packetNumber;
      EEPROM.put(0, my_sx1272config);
#endif

      printf("%s","LoRa pkt size ");
      printf("%d", pl);
      printf("\n");
      
      printf("%s","LoRa pkt seq ");
      printf("%d", sx1272.packet_sent.packnum);
      printf("\n");
    
      printf("%s","LoRa Sent in ");
      printf("%ld", endSend-startSend);
      printf("\n");
          
      printf("%s","LoRa Sent w/CAD in ");
      printf("%ld", endSend-sx1272._startDoCad);
      printf("\n");

      printf("%s","Packet sent, state ");
      printf("%d", e);
      printf("\n");

      printf("%s","Remaining ToA is ");
      printf("%d", sx1272.getRemainingToA());
      printf("\n");
        
#if defined LOW_POWER && not defined ARDUINO_SAM_DUE
      printf("%s","Switch to power saving mode\n");

      e = sx1272.setSleepMode();

      if (!e)
        printf("%s","Successfully switch LoRa module in sleep mode\n");
      else  
        printf("%s","Could not switch LoRa module in sleep mode\n");
        
      FLUSHOUTPUT
      wait_ms(50);

#ifdef __SAMD21G18A__
      // For Arduino M0 or Zero we use the built-in RTC
      //LowPower.standby();
      rtc.setTime(17, 0, 0);
      rtc.setDate(1, 1, 2000);
      rtc.setAlarmTime(17, idlePeriodInMin, 0);
      // for testing with 20s
      //rtc.setAlarmTime(17, 0, 20);
      rtc.enableAlarm(rtc.MATCH_HHMMSS);
      //rtc.attachInterrupt(alarmMatch);
      rtc.standbyMode();

      printf("%s","SAMD21G18A wakes up from standby\n");      
      FLUSHOUTPUT
#else
      nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD + random(2,4);

#if defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__
      // warning, setTimer accepts value from 1ms to 65535ms max
      timer.setTimer(LOW_POWER_PERIOD*1000 + random(1,5)*1000);// milliseconds

      nCycle = idlePeriodInMin*60/LOW_POWER_PERIOD;
#endif          
      for (int i=0; i<nCycle; i++) {  

#if defined ARDUINO_AVR_PRO || defined ARDUINO_AVR_NANO || defined ARDUINO_AVR_UNO || defined ARDUINO_AVR_MINI || defined __AVR_ATmega32U4__ 
          // ATmega328P, ATmega168, ATmega32U4
          LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
          
          //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, 
          //              SPI_OFF, USART0_OFF, TWI_OFF);
#elif defined ARDUINO_AVR_MEGA2560
          // ATmega2560
          LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
          
          //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF, 
          //      TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART3_OFF, 
          //      USART2_OFF, USART1_OFF, USART0_OFF, TWI_OFF);
#elif defined __MK20DX256__ || defined __MKL26Z64__ || defined __MK64FX512__ || defined __MK66FX1M0__
          // Teensy31/32 & TeensyLC
#ifdef LOW_POWER_HIBERNATE
          Snooze.hibernate(sleep_config);
#else            
          Snooze.deepSleep(sleep_config);
#endif
#else
          // use the wait_ms function
          wait_ms(LOW_POWER_PERIOD*1000);
#endif                        
          printf("%s",".");
          FLUSHOUTPUT
          wait_ms(10);                        
      }
      
      wait_ms(50);
#endif      
      
#else
      printf("%ld ", nextTransmissionTime);
      printf("%s","Will send next value at ");
      // use a random part also to avoid collision
      nextTransmissionTime=millis()+(unsigned long)idlePeriodInMin*60*1000+(unsigned long)(rand()%60+15)*1000;
      printf("%ld", nextTransmissionTime);
      printf("\n");
  }
#endif

}//  fin boucle

//return (0);

}