Moteino RFM69 on 3.7V LiIon battery

Started by obstler42, January 20, 2015, 10:45:40 AM

obstler42

I'm currently testing running a Moteino wireless node powered from a single 3.7V 18650 LiIon battery connected directly to the Moteino 3.3V pin. So far a DHT22 test sensor node has been running fine for a couple of hours, but I noticed that the battery voltage is rather high in the beginning: when I started out the voltage as measured on the Moteino was 3.94V (now down to 3.91V an hour later). Now the ATMega328P and most sensors should not have a problem with that, but the RFM69HW datasheet states an absolute maximum of 3.9V and operating range of 2.4V - 3.6V. This has me worried about damaging the RFM69 radio...

Has anyone been running Moteino/RFM69HW from unregulated 3.7V LiIon batteries with similar high output, bypassing the onboard voltage regulator for any length of time? Any reports of damaged radios from too high LiIon voltages?

Felix

You should avoid using the "3.3" pin. Some people have used it with voltages lower than 3.3 and that is fine although not endorsed or recommended. That pin is really meant as an OUTPUT not INPUT. Use VIN instead which requires at least 3.5V to be stable (to surpass and allow for the regulator dropout). The RFM69 abs max is 3.6V, go beyond that and your RF end will fry out even if the digital side might survive, you will pull your hairs wondering why the radio acts weird or doesn't work.

obstler42

OK.. I wanted to avoid the overhead of the voltage regulator when running from a battery as this should allow for much longer runtimes. But of course I don't want to damage it ;)

Another question: how can you measure (and send) the battery voltage when it is connected to VIN?

TomWS

Quote from: obstler42 on January 20, 2015, 11:07:11 AM
OK.. I wanted to avoid the overhead of the voltage regulator when running from a battery as this should allow for much longer runtimes. But of course I don't want to damage it ;)

Another question: how can you measure (and send) the battery voltage when it is connected to VIN?
As Felix points out, putting 3.7V into the 3.3V pin will possibly damage your Moteino radio.  It will also draw more current than running through the voltage regulator and, therefore, will actually shorten your battery life.

It's all about mAH, increase the current and you'll shorten the life. 

Using the regulator will also extend the amount of useful life you'll get out of the battery by letting your system run even when the LiPo is discharged down to 3.2V.   You should not run the LiPo below this voltage, however.

Finally, if you use a high resistance divider (I use 1.82M & 3.3M divider) off your Vin with the tap feeding an analog input pin, you'll be able to read voltage directly in 10mV resolution by:

v = analogRead(BAT_PIN)/2;

3.7V would produce a value of 370 and the divider won't introduce a significant drain on the battery.

Tom
I just noticed that you said Lithium Ion, not Lithium Poly, is that what you meant?

obstler42

ok, I'll keep it on VIN from now on.

For measuring voltage you connect: VIN -- 1M8R -- A0 -- 3M3R -- GND ?

Regarding the batteries: I'm using standard 3.7V 18650 LiIon batteries; LiPoly is basically the same in non-round shapes.

TomWS

Quote from: obstler42 on January 21, 2015, 03:40:12 PM
ok, I'll keep it on VIN from now on.

For measuring voltage you connect: VIN -- 1M8R -- A0 -- 3M3R -- GND ?
Yes.  This will give you a voltage at A0 = Vin *3.3M/5.12M and a digital value of VA0 * 1023 /3.3, or Vin * 1023 / 5.12.  The highest voltage you can input is 5.12Volts, which will give you a digital value of 1023 or, divided by 2, 511 +/- 1 bit.  3.3V will give you 660(330), 3.7V, 740(370), etc...  Easy math.
Quote from: obstler42 on January 21, 2015, 03:40:12 PM
Regarding the batteries: I'm using standard 3.7V 18650 LiIon batteries; LiPoly is basically the same in non-round shapes.
and not as physically robust... 

Sounds good.  Have fun!
Tom

obstler42

The external battery reading works fine; however I cannot combine it with reading the internal voltage. I'm using the following code:

int readVcc() {   // return vcc voltage in millivolts
	long result;
	// Read 1.1V reference against AVcc
	ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
	delay(2);             // Wait for Vref to settle
	ADCSRA |= _BV(ADSC);  // Convert
	while (bit_is_set(ADCSRA, ADSC));
	result = ADCL;
	result |= ADCH << 8;
	result = 1126400L / result; // Back-calculate in mV
	int t = (int)result;
	return t;
}


This apparently messes up something, as doing an analogRead() afterwards results in various different and incorrect results. I'm not quite sure anymore where I found that readVcc() code, but it produces quite good results, but obviously I don't want to mess up analogReads ;) Anyone know how to fix analogReads() after that code, or some other code to read internal voltage that doesn't mess anything else up?

thanks.

TomWS

Quote from: obstler42 on January 22, 2015, 04:59:59 AM
The external battery reading works fine; however I cannot combine it with reading the internal voltage. I'm using the following code:

int readVcc() {   // return vcc voltage in millivolts
	long result;
	// Read 1.1V reference against AVcc
	ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
	delay(2);             // Wait for Vref to settle
	ADCSRA |= _BV(ADSC);  // Convert
	while (bit_is_set(ADCSRA, ADSC));
	result = ADCL;
	result |= ADCH << 8;
	result = 1126400L / result; // Back-calculate in mV
	int t = (int)result;
	return t;
}


This apparently messes up something, as doing an analogRead() afterwards results in various different and incorrect results. I'm not quite sure anymore where I found that readVcc() code, but it produces quite good results, but obviously I don't want to mess up analogReads ;) Anyone know how to fix analogReads() after that code, or some other code to read internal voltage that doesn't mess anything else up?

thanks.
Read the Arduino Reference on analogReference() (in fact it would be useful to read the whole section on analog input), you'll need to set this back to VDD after the above call.

Tom

Tomega3

Here is a readVcc function that I use on UNOs, Mega 2560's and 1284p (Moteino Mega)
I found it on the net awhile ago and modified it for 1284p support.
Let me know if it helps.
Tom

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__) || defined(__AVR_ATmega1284P__)
    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

  #if defined(__AVR_ATmega2560__)
    /****
    it took me a while to figure-out the problem, but on MEGA 2560, immediately after analogRead(A8), ADCL started returning zero. 
    So every attempt to read from A8-A16 on Arduino MEGA will damage the functionality of readVcc().
    I've resolved the problem by adding:
    ADCSRB = 0;
    just before
    delay(2);
    **********************************/
    //ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560

    ADCSRB = 0;
  #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
}

TomWS

Here is what I have in my library.  Note that Tomega's code handles all ATMega variants while this is only for ATMega32P.

KEY part of this code is saving and restoring the ADMUX register on entrance/exit.  This way you aren't mucking up the Arduino ADC library (too much, anyway).

unsigned int readVcc() {
  unsigned int result;
  byte saveADMUX;
  
  saveADMUX = ADMUX;
  
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  
  ADMUX = saveADMUX;  // restore it on exit...
  return result;
}

Another thing to consider is that the Moteino ARef input has a capacitor on it that will cause a delay if you switch voltage references.  You need to allow extra time for this capacitor to settle within 0.1% of its final value BEFORE converting.

Tom

MPParsley

I have a 3.7v 7.4wh 2000mah LiPo battery.
Can I assume it is safe to directly connect the LiPo battery to the GND and VIN pins?

Felix

Quote from: MPParsley on January 29, 2017, 07:13:30 AM
I have a 3.7v 7.4wh 2000mah LiPo battery.
Can I assume it is safe to directly connect the LiPo battery to the GND and VIN pins?

Yes.