Project Won't Run On Battery

Hello all,

Been a while since I've been here. Having lots of family issues. But anyhoo, not going to bore you with details. I'm working on several projects (garage door opener, GPS Tracker, Laser Tripwire) which are all working flawlessly thanks to your previous guidance, but I've ran into a problem. On all of these projects I added a 3.7 volt li-ion battery so I can move the unit from one place to another without losing power. The battery isn't meant to last a long time, just long enough to unplug it and move it to another location.

I'm using a TP4056 charging module to charge the battery, which as far as I know IS charging, but when I unplug the unit the power is lost. I've added a photo from the website EasyEDA of my schematic. Please forgive me for my lack of knowledge in the schematic design. I wanted to make a PCB, and it works great except for the design lol.

I've also included my code, which is really inconsequential I think, but I figured that someone may ask for it. Thank you sincerely for any direction you all can give me.

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
SoftwareSerial GSM(10, 11);
SoftwareSerial neo(8, 9);
String textMessage;
String lampState;
String weekday = "";

double lati = 0;
double longi = 0;
double speed = 0;
uint32_t direction = 0;
uint32_t num_sat = 0;
double elevation = 0;

#define redLed    3
#define greenLed  5
#define blueLed   6
const int relay = 12;
TinyGPSPlus gps;
const int timezone_hours = -4;
const int timezone_minutes = 0;
int utcsecond = 0;
int utcminute = 0;
int utchour = 0;
int utcday = 1;
int utcmonth = 1;
int utcyear = 2021; 
int localminute = 0;
int localhour = 0;
int localday = 1;
int localmonth = 1;
int localyear = 2021; 

uint32_t daycount = 0;

void setup() {

  pinMode(redLed, OUTPUT);
  pinMode(greenLed, OUTPUT);
  pinMode(blueLed, OUTPUT);
  pinMode(relay, OUTPUT);
  digitalWrite(redLed, HIGH);
  delay(500);
  digitalWrite(redLed, LOW);
  digitalWrite(greenLed, HIGH);
  delay(500);
  digitalWrite(greenLed, LOW);
  digitalWrite(blueLed, HIGH);
  delay(500);
  digitalWrite(blueLed, LOW);
  digitalWrite(relay, HIGH);
  Serial.begin(115200);
  GSM.begin(9600);
  neo.begin(9600);
  GSM.listen();
  delay(5000);
  digitalWrite(greenLed, HIGH);
  Serial.print("GSM ready...\r\n");
  GSM.print("AT+CMGF=1\r\n");
  delay(1000);
  GSM.print("AT+CNMI=2,2,0,0,0\r\n");
  delay(1000);
  digitalWrite(greenLed, LOW);

}


void loop() {
  GSM.listen();
  delay(2);
  while (GSM.available() > 0) {
    digitalWrite(blueLed, HIGH);
    textMessage = GSM.readString();
    Serial.print(textMessage);
    delay(10);
    digitalWrite(blueLed, LOW);
  }
  neo.listen();
  if (textMessage.indexOf("ON") >= 0) {
    String array_string[20];
    string_splitting(textMessage,array_string,'\"');
    String NUMBER = array_string[1];
    digitalWrite(relay, LOW);
    lampState = "ON";
    Serial.println("Vehicle set to ON\r\n");
    textMessage = "";
    GSM.println("AT+CMGS=\"" + NUMBER + "\"\r");
    delay(500);
    GSM.print("Vehicle set to ON\r");
    GSM.write(0x1a);
    delay(1000);
    GSM.println("AT+CMGD=1,4");
    digitalWrite(redLed, LOW);
    digitalWrite(greenLed, HIGH);
    delay(5000);
    digitalWrite(greenLed, LOW);

  }
  if (textMessage.indexOf("OFF") >= 0) {
    String array_string[20];
    string_splitting(textMessage,array_string,'\"'); 
    String NUMBER = array_string[1]; 
    digitalWrite(relay, HIGH);
    lampState = "OFF";
    Serial.println("Vehicle set to OFF\r\n");
    textMessage = "";
    GSM.println("AT+CMGS=\"" + NUMBER + "\"\r");
    delay(500);
    GSM.print("Vehicle set to OFF\r");
    GSM.write(0x1a);
    delay(1000);
    GSM.println("AT+CMGD=1,4");
    digitalWrite(greenLed, LOW);
    digitalWrite(redLed, HIGH);
    delay(5000);
    digitalWrite(redLed, LOW);
  }
    
  if (textMessage.indexOf("GETLOC") >= 0) {
    Serial.println("_____________________________________");
    smartDelay(1000);
    String array_string[20];
    string_splitting(textMessage,array_string,'\"'); // 
    String NUMBER = array_string[1];  
    Serial.println("GPS data Recived\r\n");
    textMessage = "";

    // get some data and store it to variables
    speed = gps.speed.mph();
    direction = gps.course.value();
    num_sat = gps.satellites.value();
    elevation = gps.altitude.feet();
    
    Serial.print("Speed: ");
    Serial.print(speed);
    Serial.println(" Miles Per Hour");
    Serial.print("Number of Satellites: ");
    Serial.println(num_sat);
    Serial.print("Raw course in 1/100 degrees = "); 
    Serial.println(direction);
    Serial.print("Elevation: ");
    Serial.print(elevation);
    if (elevation > 0) Serial.println(" ASL");
    if (elevation < 0) Serial.println(" BSL");
    Serial.println("_____________________________________");
    GSM.println("AT+CMGS=\"" + NUMBER + "\"\r");
    delay(500);
    String pesan = "https://maps.google.com/?q=" + String(lati, 8) + "," + String(longi, 6);

    if (gps.time.isValid()) {
      // OK, so here we grab the UTC date and time
      utcyear = gps.date.year();
      utcmonth = gps.date.month();
      utcday = gps.date.day();
      utchour = gps.time.hour();
      utcminute = gps.time.minute();
      utcsecond = gps.time.second();

      Serial.println("UTC =  "+String(utchour)+":"+String(utcminute)+":"+String(utcsecond));

      localyear = utcyear;
      localmonth = utcmonth;
      localday = utcday;
      localhour = utchour + timezone_hours;
      localminute = utcminute + timezone_minutes;
      doLocalTimeCarries();
    }

    GSM.print("Date: ");
    daycount =  (localyear - 1601) * ((uint32_t)365);
    daycount += (localyear - 1601) / 4;
    daycount -= (localyear - 1601) / 100;
    daycount += (localyear - 1601) / 400;
    for (int i = 1; i < localmonth; i++) {
      daycount += days_in_month(localyear, i);
    }
     daycount += localday;
    switch (daycount % 7) {
      case 0:
        GSM.print("Sun  ");
        break;
      case 1:
        GSM.print("Mon   ");
        break;
      case 2:
        GSM.print("Tue   ");
        break;
      case 3:
        GSM.print("Wed   ");
        break;
      case 4:
        GSM.print("Thu   ");
        break;
      case 5:
        GSM.print("Fri   ");
        break;
      case 6:
        GSM.print("Sat   ");
        break;
      default:
        GSM.print("Bad   ");
        break;
    }
    GSM.print(localmonth);
    GSM.print("/");
    GSM.print(localday);
    GSM.print("/");
    GSM.println(localyear);
    GSM.print("Time: ");
    if (localhour < 10) GSM.print("0");
    GSM.print(localhour);
    GSM.print(":");
    if (localminute < 10) GSM.print("0");
    GSM.print(localminute);
    GSM.print(":");
    if (utcsecond < 10) GSM.print("0");
    GSM.println(utcsecond);
    GSM.print("Speed: ");
    GSM.print(speed);
    GSM.println(" MPH");
    GSM.print("Direction: ");
    // GSM.println(direction / 100.0);
    if  (direction < 1125) GSM.println("North");
else if (direction < 3375) GSM.println("NNE");
else if (direction < 5625) GSM.println("NE");
else if (direction < 7875) GSM.println("ENE");
else if (direction < 10125) GSM.println("East");
else if (direction < 12375) GSM.println("ESE");
else if (direction < 14625) GSM.println("SE");
else if (direction < 16875) GSM.println("SSE");
else if (direction < 19125) GSM.println("South");
else if (direction < 21375) GSM.println("SSW");
else if (direction < 23625) GSM.println("SW");
else if (direction < 25875) GSM.println("WSW");
else if (direction < 28125) GSM.println("West");
else if (direction < 30375) GSM.println("WNW");
else if (direction < 32625) GSM.println("NW");
else if (direction < 34875) GSM.println("NNW");
else GSM.println("North");
    GSM.print("Number of Satellites: ");
    GSM.println(num_sat);
    GSM.print("Elevation: ");
    if (elevation >= 0) {
      GSM.print(elevation);
      GSM.println(" Feet ASL");
    }
    else {
      // negative elevation
      GSM.print(-elevation);
      GSM.println(" Feet BSL");
    }
    // GSM.print(" Feet");
    // if (elevation > 0) GSM.println(" ASL");
    // if (elevation < 0) GSM.println(" BSL");
    GSM.print(pesan);
    GSM.write(0x1a);
    delay(1000);
    GSM.println("AT+CMGD=1,4");
  }
}


static void smartDelay(unsigned long ms) {
  unsigned long start = millis();
  do {
    neo.listen();
    delay(2);
    while (neo.available())
      gps.encode(neo.read());
  } while (millis() - start < ms);
  lati = gps.location.lat();
  longi = gps.location.lng();
  Serial.println(lati, 8);
  Serial.println(longi, 6);
}

void string_splitting(String splitMsg, String bb[], char delimiter) {
  int counter = 0 ;
  for ( int i = 0 ; i < splitMsg.length() ; i ++ ) {
    if ( splitMsg.charAt(i) == delimiter ) {
      counter++;
    }
    else {
      bb[counter] +=  splitMsg.charAt(i) ;
    }
  }
}


void doLocalTimeCarries() {
  // This function does the carries for the local time:
  // it exchanges 60 minutes for 1 hour,
  // 24 hours for 1 day, and so forth.
  if (localminute < 0) {
    // not enough minutes, so move back to previous hour
    localminute += 60;
    localhour--;
  }
  else if (localminute >= 60) {
    // too many minutes, so move forward to next hour
    localminute -= 60;
    localhour++;
  }
  if (localhour < 0) {
    // not enough hours, so move back to previous day
    localhour += 24;
    localday--;
  }
  else if (localhour >= 24) {
    // too many hours, so move forward to next day
    localhour -= 24;
    localday++;
  }
  if (localday < 1) {
    // not enough days, so move back to previous month
    localmonth--;
    if (localmonth < 1) {
      // not enough months, so move to previous year
      localmonth += 12;
      localyear--;
    }
    localday += days_in_month(localyear, localmonth);
  }
  else if (localday > days_in_month(localyear, localmonth)) {
    // too many days, so move forward to next month
    localday -= days_in_month(localyear, localmonth);
    localmonth++;
    if (localmonth > 12) {
      // too many months, so move to next year
      localmonth -= 12;
      localyear++;
    }
  }
}

int days_in_month (int y, int m) {
  // Essential helper function for advancing time:
  // it gives the number of days in month m of year y.
  // Fourth, eleventh, ninth, and sixth,
  // thirty days to each we fix. 
  if ((m==4)||(m==11)||(m==9)||(m==6)) return 30; 
  // Every other, thirty-one,
  // except the second month alone,
  if (m!=2) return 31;
  // which hath twenty-eight, in fine,
  // till leap-year give it twenty-nine.
  if ((y%400)==0) return 29; // leap year
  if ((y%100)==0) return 28; // not a leap year
  if ((y%4)==0)   return 29; // leap year
  return 28; // not a leap year 
}
1 Like

Does it happen as you unplug the external 12VDC, or the moment you toggle the power switch that selects between the jack input and the battery? I'd expect the latter to be the main problem; can you confirm, please?

Yes, I leave the power switch on and simply unplug the unit, then there's no power.

The toggle switch is single pole. I forgot to mention that.

OK, yeah, gotcha; I missed that.

Anyway, I think the obvious route to take here would be to use a proper power path management IC that handles the switchover from one source to another. There are plenty of options to choose from offered by several manufacturers; some are integrated systems that include battery charging functionality.

I suppose you could try to kludge something with some PMOSFETs, diodes, a big buffer cap to bridge the temporary gap etc. It may or may not work well; IDK.

The way it's done now I can see how it might present problems; does the MT3608 respond quick enough as the 12VDC supply drops out? And, to begin with - is the MT3608 suitable to be used this way to begin with? You're essentially setting up two power supplies to battle each other and apparently (speculation on my part) this results in either one of them (probably the MT3608) going into safety shutdown and only coming back up after the 12VDC external supply has been gone for some time. The MT3608 datasheet is kind of brief and doesn't touch upon this scenario, which should in itself be a sufficient hint to not use it this way to begin with. It's apparently not specified to work this way, so there's no way to know whether it can work at all, let alone reliably so.

1 Like

Okay, I didn't think to look at the use of MOSFET's so I'll have to research it and go from there. Thanks a bunch for your help. Have a great day.

Sure thing. Note that I'd recommend to skip MOSFETs for now and look into power path IC's that handle the switching in one convenient package for you. Only if you can't find anything that suits your needs (which would be hard to imagine), revert to DIY-ing something with discrete components.

What are the output voltages of the step-down and step-up converters?

You might try inserting a diode into the line coming out of the MT3608. That would give you a wired-OR circuit where the supply with the highest voltage will power the load, but they won't interfere with each other.

Edit: However, there seems to be a basic problem in that the output of the step-up converter feeds back into the input of the charger, which I don't think is permitted. The charger should be powered only by the 12V external input after step-down. Then the diode wired-OR would be between the output of the two converters, with the step-up having a lower output voltage than the step-down, both going into Vin. Something like this:

Backup power

2 Likes

Ahh! I just saw that. So I removed the trace that made the charger feed back into its self. Also added the diode you mentioned and hopefully it will work now. I'm headed down south right now so I'll mock it up tomorrow. Thanks for your help, I would have drove myself nuts trying to find that.

1 Like

No, that diode doesn't really change anything. You still have the output of the boost converter feeding into the input of the buck converter, which feeds into the charger. It's still circular.

And what are the voltages coming out of the converters?

Oh, you said it should come out of the boost converter? Im not at home at the moment so I'll have to look at it when I get there. So on the external plug there's 12 volts coming in, which steps down to 5v. On the battery side there's 3.7v coming in which gets stepped up to 12v, then to the step down converter, which is 5v. I may have made it 5.5v, or 6v, just to accomidate the fan and all the LED's.

Which in itself is a bit of an odd roundabout and inefficient way of making 5V from a nominal 3.7V battery voltage.

1 Like

I did it that way to match the 12v so by the time it comes out of the step down converter, it's all 5v. Yeah I probably did it wrong, I really have no odea what I'm doing. I use google and youtube to get a base education lol.

Is the 12V coming from a car battery, or is it mains powered? Also, if the buck converter outputs 5V, then you have 5V going to the Nano's Vin pin. Vin is the input to the Nano's 5V regulator, and needs at least 7V to work properly. So maybe the 5V supply should be connected to the Nano's 5V pin.

I think part of the problem is that you're trying to switch off the device (the Nano and other stuff) and the battery output with a single switch, and I don't think that works with this setup.

Can you give us a feel for how much current your circuit draws? Is there any reason why the MT3608 couldn't provide all the needed current?

I'm thinking of something like this:

Backup power_0001

It would let you turn off the device with a single switch, but battery charging could continue even when that switch is turned off. That's the way it's typically done. Normally the diode in the battery line would be a mosfet so as to prevent the voltage drop. But in your case you're only looking for brief battery power during transit, so the shottky diode should be good enough.

I'm actually using both mains and 12 volt from my truck. At least other projects need to do this. From the mains, I have a wall wart that reduced 120vac to 12vdc. Not sure of the current draw but it can't be much so I think the MT3608 is plenty sufficient for my needs. So it looks like I need a double pole switch? After looking at the diagram I see that no matter what I do, the battery circuit back feeds into its self. Is there a way to achieve an automatic switch over? Or am I getting too far into the weeds?

Won't my circuit in post #14 work? It just uses one switch, and eliminates the feedback.

And what about the Vin question?

Here's what I've got, I followed your schematic and it looks like I have 12v coming in from the outside. Then straight into the buck converter. After it goes through the series of diodes and such, I boost the voltage to 7 volts, and use the nano's 5 volt pin to power everything else. I still haven't mocked it up yet, just got home a little bit ago but I'll work on it tonight.

I also found the error in using the 5v pin vs the VIN pin. The picture doesn't show it but I moved all the accessories down to the 5v pin. And I don't know if I thanked you yet, if not, then please accept my sincere thanks for your help.

I still see three red junction dots on the Vin line. If that's at 7V, those should be on the 5V pin. Edit: ok, you fixed that.

Otherwise it looks ok to me, except remember that the 5V regulator on the Nano is a linear regulator, which means it will heat up if the Nano and other powered devices draw a lot of current, such as the relay coil. If you make the boost converter output 5V, and feed that to the Nano 5V pin, and to everything else, then the MT3608 will bear the entire burden, and the linear regulator will be bypassed. That's going to be more efficient.

I didn't notice before, but you need a regular diode across the relay coil so it won't spike when turned off.

1 Like

Does this look good for the diode between the 5v pin and the relay coil? As for the boost converter outputting 5v, does this eliminate the need for the VIN pin?