Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

low power and TTN #1

Open
FritzOS opened this issue Jan 10, 2024 · 7 comments
Open

low power and TTN #1

FritzOS opened this issue Jan 10, 2024 · 7 comments

Comments

@FritzOS
Copy link

FritzOS commented Jan 10, 2024

please find below an example which sends something to TTN. Power consumption is 19uA.

/*
* Example: Send battery voltage to TTN 
* tested with HELTEC WiFi LoRa 32(V3):
*     power consumption when sleeping: 0.019 mA
* tested with HELTEC Wireless Stick(V3):
*     power consumption when sleeping: 0.019 mA
*
* put your devEui and appKey in lines 80 and 82 !
*/
#include "Arduino.h"
#include <Wire.h>         
#include "HT_SSD1306Wire.h"
#include <LoRaWan_APP.h>
#include <CayenneLPP.h>

//#define TRACE         // print debugging information
#ifdef TRACE
  uint32_t loop_counter;
#endif

#define MAXTIMETTNLOOP 180000     // after MAXTIMETTNLOOP milliseconds, it is assumed that the TTN connection was unsuccessful.
uint32_t startloopTTN;            // millis() at which loop_TTN() was started

//-------------------------------------------------------------------------------------------------------------------
//--  Display -- Display -- Display -- Display -- Display -- Display -- Display -- Display -- Display -- Display   --
//-------------------------------------------------------------------------------------------------------------------
extern SSD1306Wire display; // use display defined in LoRaWan_APP.h

void VextON(void)
{
  pinMode(Vext,OUTPUT);
  digitalWrite(Vext, LOW);
}

void VextOFF(void) //Vext default OFF
{
  pinMode(Vext,OUTPUT);
  digitalWrite(Vext, HIGH);
}

//-------------------------------------------------------------------------------------------------------------------
//-- check battery level -- check battery level -- check battery level -- check battery level -- check battery level 
//-------------------------------------------------------------------------------------------------------------------
#define Read_VBAT_Voltage   1
#define ADC_CTRL           37
#define ADC_READ_STABILIZE 10       // in ms (delay from GPIO control and ADC connections times)

float readBatLevel() {
    #ifdef TRACE
      Serial.println("readBatLevel");
    #endif
    
    pinMode(ADC_CTRL,OUTPUT);
    digitalWrite(ADC_CTRL, LOW);                
    delay(ADC_READ_STABILIZE);                  // let GPIO stabilize
    int analogValue = analogRead(Read_VBAT_Voltage);

    #ifdef TRACE
      Serial.println("BatLevel = " + String(analogValue));
    #endif

    float voltage = 0.00403532794741887 * analogValue;
    #ifdef TRACE
      Serial.println("Voltage = " + String(voltage));
    #endif
    
    return voltage;
}

//-------------------------------------------------------------------------------------------------------------------
//-- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN -- TTN ----------
//-------------------------------------------------------------------------------------------------------------------

/* OTAA para*/
uint8_t devEui[] = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99 }; // put your devEui here
uint8_t appEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t appKey[] = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99 }; // put your appKey here

/* ABP para, not used in this case*/
uint8_t nwkSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t appSKey[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint32_t devAddr =  ( uint32_t )0x00000000;

/*LoraWan channelsmask*/
uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };

/*LoraWan region, select in arduino IDE tools*/
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;

/*LoraWan Class, Class A and Class C are supported*/
DeviceClass_t  loraWanClass = CLASS_A;

/*the application data transmission duty cycle.  value in [ms].*/
uint32_t appTxDutyCycle = 60000;  // 1 minute

/*OTAA or ABP*/
bool overTheAirActivation = true;

/*ADR enable*/
bool loraWanAdr = true;

/* Indicates if the node is sending confirmed or unconfirmed messages */
bool isTxConfirmed = true;

/* Application port */
uint8_t appPort = 2;
/*!
* Number of trials to transmit the frame, if the LoRaMAC layer did not
* receive an acknowledgment. The MAC performs a datarate adaptation,
* according to the LoRaWAN Specification V1.0.2, chapter 18.4, according
* to the following table:
*
* Transmission nb | Data Rate
* ----------------|-----------
* 1 (first)       | DR
* 2               | DR
* 3               | max(DR-1,0)
* 4               | max(DR-1,0)
* 5               | max(DR-2,0)
* 6               | max(DR-2,0)
* 7               | max(DR-3,0)
* 8               | max(DR-3,0)
*
* Note, that if NbTrials is set to 1 or 2, the MAC will not decrease
* the datarate, in case the LoRaMAC layer did not receive an acknowledgment
*/
uint8_t confirmedNbTrials = 8;

//-------------------------------------------------------------------------------------------------------------------
//-- prepareTxFrame -- prepareTxFrame -- prepareTxFrame -- prepareTxFrame -- prepareTxFrame -- prepareTxFrame -------
//-------------------------------------------------------------------------------------------------------------------
static void prepareTxFrame( uint8_t port)
{
	/*appData size is LORAWAN_APP_DATA_MAX_SIZE which is defined in "commissioning.h".
	*appDataSize max value is LORAWAN_APP_DATA_MAX_SIZE.
	*if enabled AT, don't modify LORAWAN_APP_DATA_MAX_SIZE, it may cause system hanging or failure.
	*if disabled AT, LORAWAN_APP_DATA_MAX_SIZE can be modified, the max value is reference to lorawan region and SF.
	*for example, if use REGION_CN470, 
	*the max value for different DR can be found in MaxPayloadOfDatarateCN470 refer to DataratesCN470 and BandwidthsCN470 in "RegionCN470.h".
	*/
  CayenneLPP lpp(51);
  lpp.reset();

  int last_rssi = 0;
  int last_snr = 0;

  // void addGPS(uint8_t channel, float latitude, float longitude, int32_t altitude);
  // channel: The channel number to associate with this GPS data.
  // latitude: The latitude value in decimal degrees.
  // longitude: The longitude value in decimal degrees.
  // altitude: The altitude value in centimeters. This parameter represents the height above the sea level.
  lpp.addAnalogInput(1, readBatLevel()); // Channel 1: Voltage of LiPo

  appDataSize = lpp.getSize();
  memcpy(appData, lpp.getBuffer(), appDataSize);

  #ifdef TRACE
    Serial.println("appDataSize = " + String(appDataSize));
    Serial.println();
  #endif  
}

//-------------------------------------------------------------------------------------------------------------------
//-- loop_TTN --  loop_TTN -- loop_TTN -- loop_TTN -- loop_TTN -- loop_TTN -- loop_TTN -- loop_TTN -- loop_TTN ------
//-------------------------------------------------------------------------------------------------------------------
void loop_TTN()
{
  #ifdef TRACE
    Serial.println("ttn_loop " + String(loop_counter++));    
  #endif
  
  switch( deviceState )
    {
      case DEVICE_STATE_INIT:
      {
        #ifdef TRACE
          Serial.println("DEVICE_STATE_INIT");    
        #endif

        extern bool IsLoRaMacNetworkJoined; // enforce new join to TTN
        IsLoRaMacNetworkJoined = false;

        LoRaWAN.init(loraWanClass,loraWanRegion);
        break;
      }
      case DEVICE_STATE_JOIN:
      {
        #ifdef TRACE
          Serial.println("DEVICE_STATE_JOIN");    
        #endif

        display.clear();
        display.drawString(0,10,"join-TTN");
        display.display();
      
        LoRaWAN.join();

        #ifdef TRACE
          Serial.println("DEVICE_STATE_JOIN end");    
        #endif
        
        break;
      }
      case DEVICE_STATE_SEND:
      {
        #ifdef TRACE
          Serial.println("DEVICE_STATE_SEND");    
        #endif

        display.clear();
        display.drawString(0,10, "send-TTN");
        display.display();

        prepareTxFrame(appPort);
        LoRaWAN.send();
        deviceState = DEVICE_STATE_CYCLE;
        break;
      }
      case DEVICE_STATE_CYCLE:
      {
        // Schedule next packet transmission    
        #ifdef TRACE
          Serial.println("DEVICE_STATE_CYCLE");    
        #endif

        txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );
        LoRaWAN.cycle(txDutyCycleTime);
        
        #ifdef TRACE
          Serial.println("txDutyCycleTime = " + String(txDutyCycleTime));    
        #endif
        
        deviceState = DEVICE_STATE_SLEEP;
        break;          
      }
      case DEVICE_STATE_SLEEP:
      { 
        extern uint8_t ifDisplayAck;  // ifDisplayAck is defined in LoRaWan_APP.h stack
                                      // it indicates the transmission has been completed
        
        LoRaWAN.sleep(loraWanClass);

        #ifdef TRACE
          Serial.println("DEVICE_STATE_SLEEP, ifDisplayAck = " + String(ifDisplayAck));    
        #endif

        if (ifDisplayAck == 1) // message has been sent out
        {
          LoRaWAN.displayAck();
          extern int revrssi, revsnr; //indication of signal strength, defined in LoRaWan_APP.h stack 

          #ifdef TRACE
            Serial.println("rssi = " + String(revrssi) + ", snr = " + String(revsnr));    
          #endif
        }

        break;
      }
      default:
      {
        #ifdef TRACE
          Serial.println("default");    
        #endif

        deviceState = DEVICE_STATE_INIT;
        break;
      }
    }
}

//-------------------------------------------------------------------------------------------------------------------
//-- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -- Setup -------
//-------------------------------------------------------------------------------------------------------------------
void setup() {
  // initialize OLED
  VextON();
  delay(100);
  display.init();
  display.setFont(ArialMT_Plain_16);

  //start serial interface
  Serial.begin(115200);
  Serial.println("Start");

  // initialize OLED display
  display.init();
  display.clear();
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_10);

  // prepare LoRaWAN
  Mcu.begin();
  deviceState = DEVICE_STATE_INIT;

  startloopTTN = millis();

  #ifdef TRACE
    loop_counter = 1;
  #endif

  Serial.println("Setup fertig");
}

//-------------------------------------------------------------------------------------------------------------------
//-- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop -- loop ---
//-------------------------------------------------------------------------------------------------------------------
void loop() {
  // very rarely does the TTN connection go into a stale status and requires a restart
  // we assume this after MAXTIMETTNLOOP milliseconds
  if((millis() - startloopTTN) > MAXTIMETTNLOOP) 
  {
    Serial.println();
    Serial.println("XXX     it is assumed that the TTN connection was unsuccessful        XXX");
    Serial.println();

    ESP.restart();
  }

  loop_TTN();
}
@JuliaSteiwer
Copy link
Owner

Hi @FritzOS , thank you very much for this code! I tested it with some static data, and also displayed the data on ThingSpeak – it works like a charm! As soon as I have access to my sensors, I will test it with them and let you know how that went. Thank you again so much for taking your time and commenting your solution here!

@xuuxij
Copy link

xuuxij commented Jan 11, 2024

I'm using a Wireless Stick Lite V3. I'm confident this will work so long as I can modify my pinmap. Do you have any suggestions for the best way I can do this with what you've created?

@xuuxij
Copy link

xuuxij commented Jan 11, 2024

NVM I think these are correct:

image

@JuliaSteiwer
Copy link
Owner

@xuuxij While I have personally not yet worked with the Wireless Stick Lite V3, I think modifying the code according to your pinmap should work – on the Heltec forum, I received a reply from another user that this code (with some adjustments) also works for Chirpstack LNS. Other than that, I'd say just try and go for it.

@FritzOS
Copy link
Author

FritzOS commented Jan 11, 2024 via email

@JuliaSteiwer
Copy link
Owner

JuliaSteiwer commented Jan 13, 2024

As soon as I have access to my sensors, I will test it with them and let you know how that went.

Okay, I tested the code with my sensors today, everything is working like a charm! I "only" have to figure out the issue with the electrical conductivity sensor, then all is well.

Update: The issue with the EC sensor could be resolved. I just realized that I didn't use your code to its full potential because I had been unaware that Vext concerns the Ve pins on the board. Tomorrow, I will check if there's still a transistor at home I could use or if I have to order another step-up converter to get the 3.3V from the Ve pin to the necessary 5V supply voltage for my sensors.

@JuliaSteiwer
Copy link
Owner

JuliaSteiwer commented Jan 19, 2024

@FritzOS Okay, so I have the following setup now (for testing purposes):
The Heltec board is powered via USB-C cable from the Laptop (-> I face the same issue when powering with a battery), and the outputs of the sensors are connected to the pins on the Heltec board. The GND and Ve pin of the Heltec board are connected to a step-up converter that increases the 3.3V input of the board to an output of 5V to supply the sensors. The GND and 5V output connections of the step-up are connected to a breadboard where the GND and 5V pins of the sensors are plugged into.

Now, if I use your code to power off the sensors during deep sleep, the following happens:

  • Neither the board nor the sensors go to deep sleep.
  • No matter if I specify a duty cycle of 1 minute, 5 minute or 30 minutes, the Heltec board will send data to TTN every few second (like 15s or less).
  • On the display, it goes "join-TTN – send-TTN – rssi: -61 snr 14, ACK RECEM, Into deep sleep in 2s", then blank for a second maybe, and then it repeats – it certainly does not enter deep sleep.

Previously, when I did not power the sensors via Ve, they also did not go into deep sleep, but at least the transmission of data was only according to the duty cycle (here: 30 minutes). Now neither works. Could you take a look at my code workingCode.ino and check if there's anything that could lead to these issues? Thank you so much in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants