Show by Label

Monday, May 18, 2015

_HowTo: Fully automatic PSU with UPS for the RPi

This is a post I wrote for the raspberrypi.org forum in early 2015, to show how to build a power supply unit (PSU) that would handle the starting and stopping/restarting of the Pi fully automatically based on available main power. It also employs a battery powered Universal Power Supply (UPS) to make sure that the Pi can shutdown properly. This design is specifically made for embedded applications (remote locations), file- and webservers, MediaCenters etc. If you cannot tolerate a brown-out or crash, this supply is for you.

ps pcb.JPG


One of the main goals was to make it easy for DIY hobbyists with limited resources to build this add on to the Pi.

Here is the link : https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=102015

Enjoy!
paulv


Below is the text from the RPi Forum all in one place:


A while ago I decided to build another power supply for the Pi, after having seen so many power related issues on this Forum.

Here are my list of goals:
  • 1. Provide an accurate and stable 5V DC supply , measured on TP-1 and TP-2
    2. Design it such that a large number of DC supplies can be used
    3. Fully automatic boot, shutdown, powerdown and restart mechanism.
    4. Protect the SD card from corruption due to power issues
    5. Ride out brown-outs or a mains drop with a minimum of a 1 Hr. period
    6. Use a prototype board to build the circuit with through-hole parts.
    7. Use parts that are easily available, inexpensive and avoid SMD if possible.
    8. Make the design as simple as possible for non engineers to understand and build
    9. Design it for embedded and desk-top applications
    10. A complete How-To that can also be used in a class room project.
There are already several solutions available to shutdown the Pi when the power is about to go away, or to put the Pi to sleep in order to turn the power off. Most of us rely on the fact that when you put the wall-wart that supplies the 5V DC to the Pi into the mains, it will boot automatically. Shutting it down easily or automatically is something that is not part of the Pi design, so we’ll add that functionality.
The Pi is also critical to the voltage level of 5V DC supply, especially if you connect hardware to the USB ports. Also, there are too many 5V wall-warts and USB cables (chargers) that are simply not up to the task. They cannot supply the current needed, or there is too much of a voltage drop in the USB cable before it reaches the Pi. The goal is to supply 5V, as measured between TP-1 and TP-2 on the Pi PCB.
Worse, if there are brown-outs in the main supply, this typically means that the Pi will lose power and will crash. If you have no provisions for a proper shutdown process, and the Pi gets locked-up somehow, most just “pull the plug” to reset the Pi. Not only do you lose whatever data the Pi was manipulating or worse, the SD card can become corrupted if the Pi was writing to it.

One solution is to use a battery supply to ride out the main power problems, or to automatically do a shutdown and again there are several samples on how to implement that too. (I also published a couple of variations to that theme on this forum)
However, this means that if the Pi is Halted, you have to do something to let the Pi reboot again. You either pull the power plug and stick it back in, or if you have added a button to the J6 connector, or to the GPIO pin 5 (GPIO-SCL) and GPIO pin 6 (GND) you can use that to wake it up from the Halt state.

Needless to say, this is cumbersome, so again there are some solutions available to make this more automatic as well. The majority of these solutions cut the power to the Pi, or send a reset signal to the above pins. Most solutions I found involve a single chip processor to do all this.
Two years ago I set out to design such an “automatic” supply for my “embedded” project, a web based thermostat controller. For six months out of a year, I am on another continent, so I needed a way to make the Pi automatically shutdown and reboot. Here is the (long) thread that documents the discovery process that resulted in a working solution: http://www.raspberrypi.org/forums/viewt ... 37&t=50470
The final solution that was the result of the discovery process has been working flawlessly ever since, but I wanted to make a new one that is simpler, and also designed in a way that others could build it easier. This will not be the most sophisticated solution from a high tech perspective, but it will work reliably while following the specifications I listed above.

Here we go:
Let’s discuss the scenarios the Pi goes through, so we can find solutions for the challenges as we go.

Boot Process
As I mentioned above, the Pi will start to boot as soon as power is applied to the micro USB connector, or to the GPIO connector. The boot process takes about 35 seconds, but this is variable based on the drivers that will be loaded, if a file check is performed, etc. The first thing we need to do is to make sure that this boot process is not interrupted by a power problem.
This can be done quite easily by using a back-up battery system that will take over through a switch if the main supply goes away.
backup.png
Simple battery backup

Two diodes are used as a switch to connect (OR) the two supplies together. Whatever supply offers the highest voltage will win the job to supply power to the Pi. If one drops, the other will take over, and the Pi will not notice this IF both supplies supply the same voltage within a small margin (100mV or less). We’re going to use a neat solution for that challenge. I always like to use Schottky diodes for this purpose. For one their voltage drop is about half of that of a normal diode which cuts the voltage lost over the junction. However you can also use the popular 1N4001 diodes. This design is based on supplying up to 1 Amp to the Pi, at the end I’ll show you what you need to do to increase that.
OK, next step.

Trickle Charger
In order to make sure that we always have full batteries when we need them, we really need re-chargeable cells and a charger.
Modern cells are a bit complicated to charge quickly, so we’ll limit ourselves to a slow maintenance charge, or trickle charge. This will only be possible with NiCad or NiMH rechargeable cells. Don’t play with Li-Ion cells, they can explode when not handled properly.
If we keep to a so called maintenance charge, or trickle charge, we don’t need an elaborate charging circuit. Just a few parts will handle that job as can be seen in the circuit below.
For the sake of this design, we’ll use a 12 V DC wall-wart supply that can supply a healthy 1Amp.
charger.png
Trickle-charger

So how does this work? Diode D1 is needed to make sure the trickle-charge current only flows where we want it to go. If there is no main supply, we don’t want the cells to supply voltage in that direction. The maintenance charging current for the cells needs to be set and we need to reduce the voltage to a safe charging voltage for the cells.

The charging voltage for NiCAD or NiMH cells is not to exceed 1,7V per cell. A Zener diode is used to set that limit, and 5.6V sets that limit for 4 cells to 1.4V each.

The charging current we need is depending on the capacity of the cells. 2200mAh cells are available for good prices and will provide plenty of juice for most applications. The recommended maintenance charge varies quite a bit, but a middle of the road factor is 0.045. This formula can be used to calculate the needed charge current : I = 2200mAh * 0.045 = ~100mA. The resistor value can be calculated with: R = V / I. V is the voltage over the resistor and that is 12V – 5.6V for the Zener and - 0.6V for the Diode junction drop = 5.8V. The resistor value R is then 5.8V / 100mA = 58Ohm. The wattage is V * I = 5.6V * 100mA = 0.56 Watt. Do not skimp on the wattage of the resistors, they will get hot. Go for 1 Watt minimum, and raise them a bit from the PCB and also keep them away from other critical parts.
To keep close to the calculated resister value, and to reduce the heat generated, we can use two resistors of 120Ohm of 1Watt each in parallel. A (sliding or toggle) switch is needed to turn off the battery voltage; otherwise our circuit will always be on.


Stable 5V supply for the Pi
As I mentioned earlier, there are just too many wall-warts that cannot supply the voltage at the required current. They were not made for the Pi requirements. On top of that, there are many USB cables that were designed for data transfer and not to supply the required constant current and voltage to the Pi, because the wires in the cables are too thin. They have resistance and will cause a voltage drop. Charge requirements are not that critical, but the Pi is picky.
If you use a wall-wart that has a USB connector, it is specified to supply 5V at the connector, not at the other end of a separate USB cable. If you have a wall-wart with an attached cable, it may be specified to supply 5V at the USB (micro) plug, but can’t deliver that. Several (cheap or copy cats of good supplies) are plain dangerous with fire hazards. Many simply don’t live up to the specs, and if you look on this forum, you can quickly see how many power related problems there are, especially with WIFI, or other devices you add to the Pi. With the newer Pi’s, you can power more USB devices, and the power requirements will be even more stringent.

There are actually two possibilities to solve this problem; you can buy a supply that absolutely supplies the right amount of Volts and Amps to the Pi, or you can use a supply that you may already have and that does that. Many of you will already have wall-warts in a box somewhere that were used to power modems, routers, USB disks, printers, laptops or whatever. They typically supply 12, 15, 24, or even higher DC voltages with 1, 2 or even 3 Amps. These supplies are designed to give power 24x7. They will also have been controlled and tested against the specifications and must adhere to safety standards.

I’m going to base this design on a 12 Volt DC wall-wart and at the end I’ll show you how you can modify the circuits to allow 5, 15 or 24 Volt DC wall-warts. For higher voltages coming from the wall-wart, you have to reduce that with an LM317T or other step-down based circuit to 24V. Make sure you have the wiring of the barrel plugs for the wall-warts correct. Some have the plus on the center pin, some the ground. If you want to be safe, place a diode between the input barrel connector and F1. Connect the anode to the connector, cathode to the fuse. It will protect the supply input for voltage reversals.

Because we will use 12 Volt as input, we need to reduce that to 5V for the Pi. However, we also need to worry about the voltage coming from the battery pack. It has to be within 100mV of the main supply, otherwise there will be a glitch when the diodes switch from one supply to the next, and the Pi will note that and could crash. Batteries do not really have a stable enough voltage, and in most combinations will not have a precise 5 V to begin with. (fully charged cells may supply 1.25 each, but more typical is 1.2V and 4 x 1.2V = 4.8 and that is already a no-no)

To keep it simple, we’re going to use a readily available circuit that will provide a neat trick. A combination of a DC to DC convertor for voltages above 5V (using a step-down convertor), and also for voltages that are below 5V (using a step-up convertor). Here is such a magical device:
DC-DC convertor.jpg
DC-DC up-/down convertor

The specifications are impressive:
  • It is using the popular LM2596S and the LM2577S
    Input Voltage: 3.5-28V
    Output Voltage: 1.25V-26V
    Current: 1A, max 3A with good cooling
This is how they do the trick. The input voltage, that can be between 3 to 28V is raised "boosted" to around 30V, and then lowered "Buck'ed" to a voltage between 1.2 and 26V that can be set by a trimmer. very simple but a little wastfull if you only need a rather low output voltage. More on that later.

I have two versions with somewhat different designs, and both already start working at an input voltage of 2.8V.
Look for “dc-dc step-up & step-down convertor” on eBay or Amazon. They typically cost around $10 US. I wish that I found this thing earlier. In previous designs, I used two separate convertors to do the same thing. At half the price, this is a cheaper solution, and also saves on real-estate.

If we put this circuit between the diode switch and the Pi, this device will regulate the 12V from the main supply, or the approx. 4.8V from the batteries to a voltage that can be set to a precise 5V, measured on the Pi circuit board (between TP1 and TP2) so we can also compensate for the loss in the cabling and connectors. Magic!
With this we are already able to supply the Pi with power during the boot process and when we’re running the application. We can now also shutdown the Pi safely into a Halted state. Note that when the Pi is Halted, it still draws about 70mA, but it is then safe to manually “pull the plug”.

How long we can charge the Pi from the batteries depends on the capacity of the cells. A widely and readily available 2000mAH cell will supply the Pi with a current draw of say 500mA for 2000/500 = 4 Hrs. Sounds like a lot, and it is, but you have to take into account that realistically, you may only get 75% of that capacity, which will still bring it to a respectable 3 Hrs. Unfortunately, if we’re drawing 500mA for 3 Hrs, it will take a very long time (15 hrs.?) to recharge the cells with a trickle charge of only 100mA as we have to do.

With the simple circuit we’re building, we have no way of knowing how full the cells are at any point in time, and we have no method to shutdown the Pi when they are getting empty. We also don’t know if there will be a brown out during this long charging period, so to be prudent, we need to limit the time we draw power from the cells. BTW, I will refer to the period that we draw power from the cells as the Uninterruptable Power Supply or UPS phase.

If you are interested in drawing power until the cells start to get depleted, I have posted a circuit earlier that measures the voltage of the cells, so the Pi can control this moment. For this design we’ll leave that out, but here is that post if you want to add it yourself : http://www.raspberrypi.org/forums/viewt ... 37&t=85350You will recognize some elements in this design too, notably the separate DC-DC up- and down convertors and you will see how the charger design has evolved over time.

The Shutdown Process
Eventually, under our control, we will need to turn the Pi off, to stop the depletion of the batteries. If we shutdown the Pi way before the cells are depleted, we should be safe.
Well, not quite. The Pi cannot turn itself off, it can only Halt itself. The power requirement drops to about 70mA for the original Pi’s, and that will continue to draw power from the cells. If the main supply does not come on soon, the cells will get empty, and that’s not good for them, and we’ll lose our backup. We need to find a way to switch off the power to the Pi. It will also make the restart a little easier, because when the main supply comes back on, the Pi will reboot automatically by design. If the Pi is in the Halt state, it already has power and now you need to find a way to reset it, in order to reboot. Possible, but that’s not what we’re going to do here.

Power-Switch
We need a power switch to disconnect the Pi from the power. Real men use relays, but were going to use an electronic switch using a P-channel MOSFET, driven by an N-channel MOSFET.
power switch.png
Power Switch

Modern MOSFET’s have a very small internal resistance (milli-Ohms) when they conduct, such that there is no loss of power that will translate into heat. The trick is to find the appropriate device, and with the voltages we’re working with (5V), it is a little more complicated to find one that we can turn fully ON, so that the resistance is indeed very low. Most of the right ones are in an SMD package, and that is what I would have liked to avoid. I have been using the P-channel Si3443DV before, and I really like that little critter, but it needs a DIL adapter to make it work in through-hole prototype boards. It’s the only exception to my not-use-SMD-parts specification, but a good one. I use the following kit from Texas Instruments, but if you look for SMD to DIL convertors on eBay or Amazon, you can get them for very little money. The trick is to solder the chip on the adapter, and this is a challenge, I know, but worth the trouble.
DIP Adapters.jpg
SMD to DIL convertor

Another MOSFET that is used a lot as a switch is the IRFD9024, not nearly as good, but does not need a DIP adapter. If you’re going to look for other MOSFETS, make sure the Gate-Source Threshold is below 4V, otherwise you cannot get it fully open and the internal resistor value may be such that it will get hot, and there will be a large voltage drop.
The TO-92 N-Channel BS170 to drive the switch is also popular and perfect for our use.


Mains Detection
In our design, we need to have a way to determine if the mains supply went down so we can control the UPS phase. This can easily be done by a voltage divider. We only need to make sure however that the voltage we feed into our circuit is not higher than we can handle. In our design, we’re going to need two different taps into the main supply, one for the Pi and one for a timer trigger. The following circuit does the job.
mains detection-1a.png
Mains Detection

To get a voltage level that is safe for the Pi, we need to try to stay close to the 3V3 input level, even though the GPIO inputs seem to be 5V “tolerant”. The resistors can be calculated as follows:
Vout = Rtop / (Rtop + Rbottom) * Vin
If we want to protect the Pi input, we not only need to worry about the voltage level, because even more important is the amount of current; that is the real killer. A 1mA current is very safe, so in our worst case, the total resistor value between the 12V and GND must be 12KOhm. A suitable resistor divider of standard values is than 8K2 and 2K7. This will give about 3V at the GPIO input port and we have a 10% safety margin if our supply is actually a little over 12V.

Because we work with 12V voltages, we need to be extra careful.
To protect the Pi for these lethal voltages, I recommend that you always protect the GPIO ports with a series resistor of 1K. It may be sufficient if you accidentally put 12V in a GPIO pin, because it will limit the current to a maximum of 15mA, but only if it is for a short moment. To protect for 5V levels, this will limit the current to 5V/1K = 5mA and that is very safe.

There are no electrical specifications published for the GPIO pins, which is unfortunate. But we can assume, proven by measurements that were made, that the GPIO inputs are clamped at 5.6V on the processor die, to make them 5V tolerant, and we should be safe now. However, it is still prudent to clamp the possible overvoltage on this input to a potential 12V source with a separate diode to the 3V3 supply. Even if we make a mistake, the Pi GPIO pin is now protected by two simple parts that only cost 20 cents.

BTW, the 5V power input on the Pi PCB is protected against higher voltages by a transient diode and a fuse, but you need to make sure you don’t trip those either. Stay below 5.2V to be safe. The fuse is bypassed when you power the Pi through the P1 GPIO header, but the transient is still functional on the 5V supply of the Pi.

To filter ripples and glitches that we don’t need to consider, we can use a 5-10uF (max) capacitor to dampen the 12V supply changes. The R/C factor for the Pi input will be about .5 Sec.

With this, we have almost all of the major components in place; we now need the controlling logic to make it all work.

Controller Logic
We have already seen that we need a way to control the power to the Pi through a Power-Switch and we also need to have a timing device for the Shutdown phase, in addition to a wake-up call for the Power-Switch. Here is where most solutions use a single chip processor, but that involves programming in assembler or C/C++, an assembler C or C++ compiler and loader and programmer. This is something most of us don’t have access too, or don’t want to go that route, and I am no different.
I have experimented and build various solutions to this problem, but most were a little complicated or not reliable enough. This time, I wanted to design something that is simple, with as few (non-critical) parts as possible, and above all, reliable and understandable for a wide audience. It should also be easily adaptable or extendable for as many other applications as possible.

The most simple and reliable design I found is one where the basic default is to remove power from the Pi a little after the shutdown, and to control the automatic start of the system by applying power again.
The implementation I use here only needs two logic functions; a timer and a watchdog. The timer will be used to guarantee clean power during the shutdown process, and to control the power-switch that will automatically turn off the power. When the main power returns, the watchdog will control the power-switch to automatically (re)boot the Pi. This sounds simple, but there are a couple of caveats lurking in the dark.

Remember that one of the key requirements is to always have enough back-up power for the Pi when we really need it. This means that we need to carefully control the time that the cells supply power when there is no mains. The program running on the Pi that controls that can only have control of the hardware when it has fully booted – of course. When we shutdown the Pi, the software program is terminated, so we need something that handles the power during the shutdown phase, and remove it from the Pi automatically.

Consider the situation that the main voltage is there, the Pi starts to boot, but during this process, the main voltage goes away again. The batteries will take over, but the Pi will be unaware of this until the controlling app starts running. This program can test the availability of the mains, and so this situation will be handled in the program. So far, so good.

When the program is up and running, it now has control of the hardware, it will be notified if there is a drop of the main voltage, can control the UPS duration and start the shutdown process if the end of the UPS phase has been reached. If power returns (a glitch) after a short period while running in UPS mode, it can terminate the UPS phase, reset the timer and start all over again. Again, so far, so good.

The Shutdown Window
The Shutdown Window is more difficult and more critical to handle. Normally, when the main power goes away, the Pi will go through the shutdown process, will Halt, and the power will be switched off. But what if it comes back while we are shutting the Pi down? We have no way of detecting that with our controlling software anymore, so the Pi is Halted, the power is switched off with our Power-Switch but the Pi cannot reboot itself unless we somehow turn the power back on through the Power-Switch. We will use a Watch-Dog to handle that case.

I have tried several variations of a design with only one timer, but because we need a bi-stable circuit to get a single time window, it needs a momentary negative slope to trigger it. The design gets ugly and complicated quickly to account for all conditions. The reason is that you cannot miss the one pulse that is needed to trigger the timer when the power comes back in all situations, or the Pi will not wake-up. A free running (a-stable) oscillator based on a voltage level instead of a pulse or edge will do the triggering reliably.
Here that is that control circuit:
Timers-1a.png
Timer Section

(VCC is 5V, tapped from the DC-DC convertor output, but before the power-switch)

Let’s start with the Watch-Dog. As soon as the main power is available, it needs to be activated. I use the Reset pin of a 555 timer chip to do that. This input is active-low, so when it is pulled high by the 12V availability, the watchdog timer is activated by a voltage level, not a single pulse. As a Watch-Dog, we need it to send pulses to the Timer, so we configure the 555 as an astable-multivibrator with a 99.9% duty cycle. To create that kind of a duty cycle, we need D1. We also want to have a period of about 55-60 seconds, and this is accomplished by the 820K and the 100uF. To create the 99.9% duty cycle, we use R3 of 820 Ohm. These values are not very critical, but will generate a short pulse every 55-60 seconds.

Three is a great website where you can read about this setup in detail : https://electrosome.com/astable-multivi ... 555-timer/

The output of the Watch-Dog timer is fed into another 555 that acts like a Timer. That Timer is used to drive the powerswitch to create the boot and shutdown power windows. It is configured as a bistable-multivibrator, meaning that when triggered, it will fire only once. We use the Trigger input of the 555 to start the timer, and it will start on a low going edge. This means that you cannot simply connect the output of the Watch-Dog directly to the Trigger input of the Timer because it will continue to be on. To create the negative edge we need, we feed the Watch-Dog pulse through a capacitor. The Trigger cannot be high, or low, so I use two 10K resistors to “tie” the Trigger level to about 2.5V, and this also makes sure that the pulse coming out the capacitor has enough energy to create the negative edge to fire the Timer (the 555 Trigger spec is 1,67V @ a VCC of 5V). The output of the Timer is fed to the Power-Gate. The period of about 45 seconds is created with the 390K and the 100uF capacitor. To avoid an output when the power comes on, I use a resistor and capacitor to create a delay on the active-low Reset pin (the 555 spec is 0.5V for this input).

As soon as our program is running and we have control after the Boot period, we need to stop the Watch-Dog from firing, and the easiest way is to pull the Reset line low through a GPIO output port. R5 avoids a tug of war with the main supply detection.
We also need to take control of the Power-Switch through our program, before the Timer runs out and will switch the power off. We use another GPIO pin as output to get control of the Power-Switch. The connection to the Power-Switch by these two signals is done with a diode OR circuit.

If we are going to shutdown the Pi, we will pull the Watch-Dog disable output high again so the main supply detection to release the Watch-Dog is back in charge. Note that when the Pi becomes Halted or is powerless, this GPIO output pin is not activated and will not affect the detection of the main supply.

When our program is running and detects a loss of the main supply, we can go through a UPS period to see if it was only a short Brown-Out, in which case it can go back to watching the next power drop. If it was a longer period of main power loss, we need to Shutdown the Pi. Our program now needs to trigger the Timer to keep the Power-Switch activated through the Shutdown period before it shuts off. It also needs to release the Watch-Dog, to prepare for a reboot when the power comes back on. The Timer will keep the Pi powered during the shutdown process and it then turns off the Power-Switch to remove power from the Pi, getting it ready for a restart.
All in all, we will need to dedicate a minimum of three GPIO ports to drive the power supply. (mains detection, Power-Switch control, Watch-Dog dis-abler)

Extra’s It would be nice to have visual indicators that will tell us what is happening, because in most embedded applications, there is no monitor. So, we will use one additional GPIO port to drive an LED, and use that to tell us what the application program is doing.
Another extra is to use yet another GPIO port as an input to react to a button. When we measure the length of time we press the button, we can create multiple functions with only one button. We will use this button to stop the application, or to do a software reboot of the Pi.

During the shutdown process, the Pi is first Halted, before we cut the power. To get an indication of the Halt state, and see if the Pi is booting, we can optionally use a special GPIO port, to help us display what is going on. By default, GPIO port 14 (TXD) shows activity during the boot process (it send some data to this serial port) and this port will be pulled low in the Halt state. We can use a P-channel MOSFET to drive an LED to show this. The LED will flicker some during the boot, and then turn off, and will turn back on when the Pi is Halted.

Finally, for many applications it is nice to be able to reset the Pi, or wake it up from the Halt state manually. We don’t need to use GPIO ports for this, although this is also possible if you momentarily short GPIO-3 to GND. These are two pins across from each other on the P1 connector. I prefer to use the dedicated P6 connector, which is directly connected to the processor. This connection benefits from a pull high resistor and has external protection diodes to 3V3 and GND (it is most likely not 5V tolerant). If you use the connector types below, you also have an additional support for the top board and get easy access to the J6 connector.
Connector.jpg
connectors

Cut two pins of each connector type. Solder the bottom version on the Pi PCB in the slot for the J6 connector, and solder the two pins from the top connector to the bottom (underside) from your "HAT" PCB.

If you use an older generation Pi (model 2 Rev A or B), you may run out of the valuable GPIO pins on the P1 connector. However, if you use the above type of connectors, you can easily create access to 4 additional GPIO ports (28, 29, 30, 31) through the P5 connector, which is the unpopulated set of holes next to the P1 connector. This will leave the P1 connector almost free. Note that in our design, I use the 1K8 pull-up resistors of the GPIO-2 (SDA) and GPIO-3(SCL) ports, so you need to add them if you use P5 ports.

The P1, P5 and P6 details can be found on the schematic of the Pi.
http://www.raspberrypi.org/model-b-revi ... chematics/

Here is the schematic of the complete solution:
Automatic PS.png
Complete Schematic

To make the supply fully automatic, we need a duplicate of the Power-Switch, and use that to remove the battery power from the DC-DC convertor when the Pi has been made powerless. Otherwise, the batteries will be drained by the power requirements of the DC-DC convertor and all the logic. The manual switch will still remove the batteries from the supply, but can be left on as default. The LED shows that the batteries are feeding the supply so there is a visual indicator.

My circuit board looks like this in an earlier version:
ps pcb.JPG
Power Supply

 
And the Pi top board:
Pi GPIO Board.jpg
Pi Top Board


I’m supplying the Pi with power through the micro USB connector, and in order to do that, I cut a good USB cable with proper power leads in two pieces and added a barrel connector to it.

Software Controlling Program
At this time, we should switch to the controlling program. Let me explain that piecemeal as well.

Here are the GPIO Ports we’ll use:

Code: Select all

# GPIO Ports
PWR_Gate_P = 17         # output GPIO-04
PWR_Sense_P = 4         # input  GPIO-17
PWR_Timer_P = 27        # output GPIO-27
PWR_Reboot_P = 2        # input  GPIO-02 (SDA with 1K8 pull-up)
System_Status_P = 3     # output GPIO-03 (SCL with 1K8 pull-up
Halt_State = 14         # output GPIO-14 (shows Halt State) not driven by this App
Here is part of the init function where we define the GPIO ports (code is not complete!):

Code: Select all

        # Use the Raspberry Pi BCM pins
        GPIO.setmode(GPIO.BCM)

        # This is the port that will trigger the Power Timer to control the
        # uninterrupted shutdown period
        # Setting the Port LOW now will stop the Watchdog Timer from firing
        # until we release it again
        GPIO.setup(PWR_Timer_P, GPIO.OUT, initial=GPIO.LOW)

        # This port must now be set HIGH to turn the PowerGate on within the
        # Timer period of 45 seconds, otherwise the Pi will become powerless
        # after the Timer runs out.
        GPIO.setup(PWR_Gate_P, GPIO.OUT, initial=GPIO.HIGH)

        # Main Power loss input, we will set this up as an interrupt further below
        GPIO.setup(PWR_Sense_P, GPIO.IN)

        # system Stop_App/Pi_Reboot button, the interrupt is defined below
        GPIO.setup(PWR_Reboot_P, GPIO.IN)

        # system status LED setup
        GPIO.setup(System_Status_P, GPIO.OUT)
        # We'll use Pulse Width Modulation to control it
        # start with a frequency of 0.5 Hz
        sys_stat = GPIO.PWM(System_Status_P, 0.5)
        # start the PWM, but with a duty cycle of 0; the LED is on with no flash
        sys_stat.start(0)

        # --- these definitions need to be after the port definitions,
        # --- otherwise the detections will not work!
        #
        # setup the event to detect a falling edge on the main power sense input
        GPIO.add_event_detect(PWR_Sense_P, GPIO.FALLING, callback=power_action, bouncetime=5000)
        #
        # setup the event detection thread for the Stop_App/Pi Reboot button
        GPIO.add_event_detect(PWR_Reboot_P, GPIO.FALLING, callback=button_action, bouncetime=5000)
        #
        # if we lost the main power already during the boot process, we need to
        # check this here and take action.
        if (GPIO.input(PWR_Sense_P) == 0) :
            power_action(PWR_Sense_P)
            # maybe power comes back during the UPS period, otherwise we'll
            # shutdown the Pi
At this moment in the initialization process, we have already turned off the Watch-Dog and enabled the Power-Gate. We will also get an interrupt if we lose the main voltage.
The interrupt is handled with this piece of code (not complete!):

Code: Select all

def power_action(PWR_Sense_P):
        ups_timer = 1
        while (ups_timer < UPS_MODE) and (GPIO.input(PWR_Sense_P) == 0): # play UPS
            sleep(1) # check every second
            ups_timer += 1
        if ups_timer < UPS_MODE :
            # The power came back!
            sleep(1) # let the system settle a bit and check it again
            if GPIO.input(PWR_Sense_P) == 1 :
                system_status(ON, 20, 0.5) # system LED flashing back to normal
                return
        # Main power still off -> starting shutdown process
        # show the user
        system_status(ON, 50, 6) # start flashing at 6 Hz
        #
        # start the shutdown process
        # Start the UPS timer by releasing the Watch-Dog
        GPIO.output(PWR_Timer_P, GPIO.HIGH)
        #
        # Now release the Power-Gate of the UPS to let the 555 timer
        # control the power to the Pi until shutdown is completed.
        GPIO.output(PWR_Gate_P, GPIO.LOW)
        # Shut the Pi down:
        subprocess.call(['shutdown -hP now "System shutdown due to main power failure" &'], shell=True) 
This program is supposed to start at boot time, and we need to be able to control it somewhat through an SSH connection to the Pi, since we will most likely run it “head-less”. The best way to handle that, and keep maximum control, is to install it as a deamon through a controlling script that we put in the /etc/initd.d directory.

Here is the daemon controller that I use for this application.
The application is called my_app_daemon.py, and the daemon controller is called my_ps_service.sh In the code I’ll explain how to install it.

Code: Select all

#!/bin/sh

### BEGIN INIT INFO
# Provides: my_ps_service
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: This deamon controls my_app_daemon to handle a Pi Power Supply
# Description: The App has code that will control the power supply (PS) to handle
# uninterrupted Boot and Shutdown windows when the main power is dropped.
# A software controlled push button controls the stopping of the App, or the reboot
# of the Pi
### END INIT INFO

# Call this shell script my_ps_service.sh
# This shell script will call my_app_daemon.py
# Copy this init script into /etc/init.d using
#       sudo cp my_ps_service.sh /etc/init.d/.
# Make sure the script is executable
#       sudo chmod 755 /etc/init.d/my_ps_service.sh
#
# At this point you should be able to start the Python script using the command
#       sudo /etc/init.d/my_ps_service.sh start
# Check its status with
#       sudo /etc/init.d/my_ps_service.sh status
# and stop it with
#       sudo /etc/init.d/my_ps_service.sh stop
#
# to install my_ps_service in the boot sequence :
# sudo update-rc.d my_ps_service.sh defaults
#
# to remove from the boot sequence :
# sudo update-rc.d my_ps_service.sh remove

# Change the next 3 lines to specify where the python script is and what you want to call it
DIR=/home/pi
DAEMON=$DIR/my_app_daemon.py
DAEMON_NAME=my_app_daemon

# make sure that my_app_daemon.py is executable and the Python shebang is present
# chmod 755 test_simple_ups.py

# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=root
# Most likely no need to change anything below this line, but read on!
# ==========================================================================
# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid

. /lib/lsb/init-functions

do_start () {
    log_daemon_msg "Starting system $DAEMON_NAME"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON
    log_end_msg $?
}
# we use the --background flag of start-stop-daemon to run our script
# in the background

do_stop () {
    log_daemon_msg "Stopping system $DAEMON_NAME"
    start-stop-daemon --stop --pidfile $PIDFILE --retry=TERM/10/KILL/5
    log_end_msg $?
}
# the --retry means that first of all a TERM -15 signal is sent
# to the process and then 10 seconds later it will check if the process is still there
# and if it is send a KILL -9 signal (which definitely does the job).
# if you catch the TERM-15 signal within the app, you can control the shutdown
# sequence and properly save all important data for a restart. You don't want
# to be caught unprepared for the Kill -9, if needed, extend the 10 seconds 
# BUT, you then also need to extend the Timer with the same amount of time! 
# Otherwise, you may lose power before you’re done.

case "$1" in

    start|stop)
        do_${1}
    ;;

    restart|reload|force-reload)
        do_stop
        do_start
    ;;

    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
    ;;

    *)
        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
        exit 1
    ;;
esac

exit 0


I have made two complete versions for the controlling code.

Bare Bones Version
This version is a simple controller program version, without any frills. It can be used if you do not use the GPIO pins on your Pi for another application. This version is great for desk-top uses, when you design software, or even for embedded desk-top applications like a media center or server.

Code: Select all

#!/usr/bin/env python2.7
#-------------------------------------------------------------------------------
# FileName:     my_app_daemon.py
# Purpose:      This program interfaces with the Pi Power & UPS hardware to
#               handle brown-outs and loss of the main power. The app will be
#               able to boot and shutdown without power issues, to protect the
#               SD card, and to preserve the running variables of the app.
#
#               This is the bare bones version
#
# Note:         All dates are in European format DD-MM-YY[YY]
#
# Author:       Paul Versteeg
#
# Created:      08-Dec-2012 and Feb-2015
#
#    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.
#
#    To get a copy of the GNU General Public License
#    go to <http://www.gnu.org/licenses/>
#-------------------------------------------------------------------------------


import os
import RPi.GPIO as GPIO
from time import sleep
import subprocess
import sys
import traceback


#===============================================================================
# Global constants & variables

# debug and test constants
DEBUG = True
TRACE = True
REBOOT = True
SHUTDOWN = True
TERMINATE_2_OS = True
RUN_AS_DAEMON = True

# NOTE:
# The GPIO_cleanup function sets ALL (used or not) Ports back to the
# default state, which is input. In our case, this will remove Power to the Pi
# and it will become powerless without any control. Also note that if you use
# another program in parallel (concurrent) that also uses GPIO ports, a
# cleanup in either program will clean ALL GPIO ports, regardless!
GPIO_cleanup = False

# constants
__author__ = 'Paul Versteeg'
VERSION = "1.0d"
OFF = False
ON = True
if DEBUG :
    UPS_MODE = 10       # During testing, the UPS provides power for 10 seconds
else:
    UPS_MODE = 10*60    # 10 minutes in normal operation, change to fit your wish
                        # Beware, it may take at least 5 x longer to re-charge due
                        # to the 100mA trickle charge

# GPIO Ports
PWR_Gate_P = 17         # output GPIO-04
PWR_Sense_P = 4         # input  GPIO-17
PWR_Timer_P = 27        # output GPIO-27
System_Status_P = 3     # output GPIO-03 (SCL with 1K8 pull-up
Halt_State = 14         # output GPIO-14 (shows Halt State) not driven by this App

disable_button_action = False


def init():
    '''
    Initializes a number of settings and prepares the environment
    before we start the main application.

    '''
    global sys_stat

    try:
        #
        # ----- setting up the GPIO Ports
        #
        if DEBUG: GPIO.setwarnings(True)
        else: GPIO.setwarnings(False)

        # Use the Raspberry Pi BCM pins
        GPIO.setmode(GPIO.BCM)

        # This is the port that will trigger the Power Timer to control the
        # uninterrupted shutdown period
        # Setting the Port LOW now will stop the Watchdog Timer from firing
        # until we release it again
        GPIO.setup(PWR_Timer_P, GPIO.OUT, initial=GPIO.LOW)

        # This port must now be set HIGH to turn the PowerGate on within the
        # Timer period of 45 seconds, otherwise the Pi will become powerless
        # after the Timer runs out.
        GPIO.setup(PWR_Gate_P, GPIO.OUT, initial=GPIO.HIGH)

        # Main Power loss input, we will set this up as an interrupt further below
        GPIO.setup(PWR_Sense_P, GPIO.IN)

        # system status LED setup
        GPIO.setup(System_Status_P, GPIO.OUT)
        # We'll use Pulse Width Modulation to control it
        # start with a frequency of 0.5 Hz
        sys_stat = GPIO.PWM(System_Status_P, 0.5)
        # start the PWM, but with a duty cycle of 0; the LED is on with no flash
        sys_stat.start(0)

        # --- these definitions need to be after the port definitions,
        # --- otherwise the detections will not work!
        #
        # setup the event to detect a falling edge on the main power sense input
        GPIO.add_event_detect(PWR_Sense_P, GPIO.FALLING, callback=power_action, bouncetime=5000)
        #
        #
        # if we lost the main power during the boot process, we need to
        # check this here and take action.
        if (GPIO.input(PWR_Sense_P) == 0) :
            subprocess.call(['logger "Unexpected Power Loss during init()"'], shell=True)
            power_action(PWR_Sense_P)
            # maybe power comes back during the UPS period, otherwise we'll
            # shutdown the Pi
        #
        # start the app hart-beat
        system_status(ON, 5, 0.2) # Normal Operation, 5% ON, 95% OFF @ 0.5 Hz
        #
        subprocess.call(['logger "UPS Supply setup is finished. Starting Main"'], shell=True)

    except Exception as e:
        traceback.print_exc(file=open("my_daemon_errlog.txt","a"))
        subprocess.call(['logger "Unexpected Exception in init()"'], shell=True)
        if GPIO_cleanup :
            # reset all used I/O pins back to normal (input)
            # NOTE, this will lower the PWR_Gate and the system will be powerless
            GPIO.cleanup()
        #
        os._exit(1) # force the exit to the OS, sys.exit(1) will NOT do that properly!



def system_status(mode, duty_c=5, freq=0.2):
    '''
    Function to drive the status LED.

    parameters : mode [ON|OFF],
                 duty_cycle [0..100] 0=on, 100=off,
                 blinking frequency in Hz
    '''
    global sys_stat

    try:
        if mode:
            sys_stat.ChangeDutyCycle(duty_c)
            sys_stat.ChangeFrequency(freq)
        else:
            sys_stat.ChangeDutyCycle(100) # LED is off

    except Exception as e:
        traceback.print_exc(file=open("my_daemon_errlog.txt","a"))
        subprocess.call(['logger "Unexpected Exception in system_status()"'], shell=True)
        return



def power_action(PWR_Sense_P):
    '''
    This function controls the Power to the Pi.

    If a loss of main power is detected, the supply acts as a UPS to see if it
    only was a glitch, if not, the Pi is shutdown.

    After the shutdown of the Pi, it will become powerless. The power supply
    will automatically turn the power on to let the Pi boot if the main
    power returns.
    '''
    try:
        subprocess.call(['logger "UPS: Main power failure detected"'], shell=True)
        # flash at 3 Hz so the user can see we have a loss of main power
        system_status(ON, 50, 3)

        sleep(1) # Let the voltage drop completely
        # we can play UPS for a while before we perform the shutdown.
        # if there was a power glitch, we'll let the Pi continue
        # if the mains is really out and we lost power, we need to shutdown the Pi.

        ups_timer = 1
        while (ups_timer < UPS_MODE) and (GPIO.input(PWR_Sense_P) == 0): # play UPS
            sleep(1) # check every second
            ups_timer += 1

        if ups_timer < UPS_MODE :
            # The power came back!
            sleep(1) # let the system settle a bit and check it again
            if GPIO.input(PWR_Sense_P) == 1 :
                system_status(ON, 5, 0.2) # system LED flashing back to normal
                subprocess.call(['logger "UPS: Main power returned within UPS period"'], shell=True)
                return

        # Main power still off -> starting shutdown process
        # show the user
        system_status(ON, 50, 6) # start flashing at 6 Hz
        #
        # add code here to handle a restart
        #
        # start the Timer and release the Watchdog
        GPIO.output(PWR_Timer_P, GPIO.HIGH)
        sleep(0.01)
        GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
        sleep(0.1)
        GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
        #
        #
        # Now release the power gate of the UPS to let the 555 timer
        # control the power to the Pi until shutdown is completed.
        GPIO.output(PWR_Gate_P, GPIO.LOW)
        #
        subprocess.call(['logger "UPS: System shutdown due to main power failure"'], shell=True)
        if DEBUG : sleep(5) # so we can see it before the shutdown
        if SHUTDOWN :
            subprocess.call(['shutdown -hP now "System shutdown due to main power failure" &'], shell=True)

            # when the shutdown sequence has started, it cannot be stopped. If the power comes back on
            # in this period, the Pi will still be left powerless. In that case,
            # the PWR_Watchdog will restart the Pi.
            #
            sleep(60) # don't return, wait for the shutdown to happen
        else:
            if GPIO_cleanup :
                # reset all used I/O pins back to normal (input)
                # NOTE, this will lower the PWR_Gate and the system will become powerless
                GPIO.cleanup()
            system_status(ON, 5, 0.2) # system LED flashing back to normal
            return

    except Exception as e:
        # in case that there is an exception, still force the shutdown!
        # if the shutdown flag has already been set, fine, otherwise, we'll have
        # to restart from scratch
        #
        traceback.print_exc(file=open("my_daemon_errlog.txt","a"))
        subprocess.call(['logger "Unexpected Exception in power_action(), reboot"'], shell=True)
        sleep(2)
        if SHUTDOWN :
            # Make sure we can finish the reboot without a power interruption
            #
           # start the Timer and release the Watchdog
           GPIO.output(PWR_Timer_P, GPIO.HIGH)
           sleep(0.01)
        GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
        sleep(0.1)
        GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
        #
            #
            # Now release the power gate of the UPS to let the 555 timer
            # control the power to the Pi until shutdown is completed.
            GPIO.output(PWR_Gate_P, GPIO.LOW)

            if GPIO_cleanup :
                # reset all used I/O pins back to normal (input)
                # NOTE, this will lower the PWR_Gate and the system will become powerless
                GPIO.cleanup()

            subprocess.call(['shutdown -F -r now "System reboot due to error in power_action()" &'], shell=True)
            sleep(60) # don't do anything anymore
        else:
            return



def main():
    '''
    The main daemon routine.

    After the initialisation, we start the loop waiting for an interrupt.
    '''
    #
    init()

    try:

        while True :
            sleep(1)
            #
            # wait for a button press to terminate or shutdown
            #
            # watch out for a loss of the main power

    except Exception:
        traceback.print_exc(file=open("my_daemon_errlog.txt","a"))
        #
        # add code here to handle a restart
        #
        # so we can restart the program again
        #
        # Make sure we can terminate properly
        # release the Watchdog
        GPIO.output(PWR_Timer_P, GPIO.HIGH)

        if GPIO_cleanup :
            # reset all used I/O pins back to normal (input)
            # NOTE, this will lower the PWR_Gate and the system will become powerless
            GPIO.cleanup()
        #
        subprocess.call(['logger "UPS: Forcefully terminating with error"'], shell=True)
        # exit en return an error code for this instance
        os._exit(1) # force the exit to the OS


if __name__ == '__main__':
    main()
The following statements in the code:

Code: Select all

subprocess.call(['logger "UPS Supply setup is finished. Starting Main"'], shell=True)
will show up in the /var/log/messages together with other system messages:

Code: Select all

Feb 26 21:51:58 raspberrypi kernel: [   23.932496] wlan0: associate with 00:21:29:b1:53:3b (try 1/3)
Feb 26 21:51:58 raspberrypi kernel: [   23.935264] wlan0: RX AssocResp from 00:21:29:b1:53:3b (capab=0x411 status=0 aid=5)
Feb 26 21:51:58 raspberrypi kernel: [   23.947476] wlan0: associated
Feb 26 21:51:59 raspberrypi kernel: [   28.156756] Adding 102396k swap on /var/swap.  Priority:-1 extents:1 across:102396k SSFS
Feb 26 21:52:01 raspberrypi logger: UPS Supply setup is finished. Starting Main
and in the /var/log/user.log file:

Code: Select all

Feb 26 14:50:41 raspberrypi logger: UPS Supply setup is finished. Starting Main
Feb 26 14:52:18 raspberrypi logger: UPS: Main power failure detected
Feb 26 14:52:20 raspberrypi logger: UPS: Main power returned within UPS period
Feb 26 14:52:24 raspberrypi logger: UPS: Main power failure detected
Feb 26 14:53:02 raspberrypi logger: UPS: System shutdown due to main power failure
Feb 26 14:53:03 raspberrypi shutdown[2320]: shutting down for system halt
Power_Timer & Watch_Dog.jpg
Start Timer & release Watchdog

Here is a screenshot that shows the signals when we decide to shutdown the Pi.
Trace A shows the pulse train that triggers the Timer, and then releases the Watchdog.
Trace B shows the start of the Timer. It starts with the 10mSec pulse and will keep the Power-Gate open for 45 sec., more than enough for the Pi to finish the shutdown.
After the Pi is Halted, it will be waiting for the release of the Power-Gate. When the 45 seconds of the Timer ends, the Pi will be made powerless, and also the batteries will be disconnected to make everything powerless.
The supply and the Pi will stay powerless until the main power comes back.
If the main power came back during the Shutdown sequence of the Pi, but before the Timer runs out, the Watch-Dog will open the Power-Gate and the Pi will start again.

The Extended Version
If you do use other GPIO ports, or use your app in a real embedded solution, you can use this extended program version that has a lot of bells & whistles that will help you to integrate the two.

I highly recommend combining the code needed to run the power supply within the code of your own application. It makes the management of the two programs and the communication between them so much easier, especially if your program also uses GPIO ports.
BEWARE of the GPIO-cleanup function!
Look at my comments in the code for an explanation.

Code: Select all

#!/usr/bin/env python2.7
#-------------------------------------------------------------------------------
# FileName:     my_app_daemon.py
# Purpose:      This program interfaces with the Pi Power & UPS hardware to
#               handle brown-outs and loss of the main power. The app will be
#               able to boot and shutdown without power issues, to protect the
#               SD card, and to preserve the running variables of the app.
#
#               This is the de-lux version, to be combined with your own app.
#
# Note:         All dates are in European format DD-MM-YY[YY]
#
# Author:       Paul Versteeg
#
# Created:      08-Dec-2012 and Feb-2015
#
#    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.
#
#    To get a copy of the GNU General Public License
#    go to <http://www.gnu.org/licenses/>
#-------------------------------------------------------------------------------


import os
import RPi.GPIO as GPIO
from time import sleep
import signal
import subprocess
import sys
import logging
import logging.handlers
import traceback


#===============================================================================
# Global constants & variables

# debug and test constants
DEBUG = True
TRACE = True
REBOOT = True
SHUTDOWN = True
TERMINATE_2_OS = True
RUN_AS_DAEMON = True

# NOTE:
# The GPIO_cleanup() function sets ALL (used or not) Ports back to the
# default state, which is input. In our case, this will remove Power to the Pi
# and it will become powerless without any control. Also note that if you use
# another program in parallel (concurrent) that also uses GPIO ports, a
# cleanup in either program will clean ALL GPIO ports, regardless!
# You can use the clean up on individual ports with GPIO.cleanup(Port),
# but you need to be careful. 
# Also remember that the Pull-up/down settings
# are written in EEPROM(?) and stay that way, also when the Pi is powered
# down, unless you change them again, or turn the pull off: 
# pull_up_down=GPIO.PUD_OFF
GPIO_cleanup = False

# constants
__author__ = 'Paul Versteeg'
VERSION = "1.0d"
OFF = False
ON = True
if DEBUG :
    UPS_MODE = 20       # UPS provides power for a period of 20 seconds
else:
    UPS_MODE = 10*60    # 10 minutes in normal operation, extend if required
                        # Beware, it may take 5-10 x longer to re-charge due
                        # to the 100mA trickle charge

# GPIO Ports
PWR_Gate_P = 17         # output GPIO-04
PWR_Sense_P = 4         # input  GPIO-17
PWR_Timer_P = 27        # output GPIO-27
PWR_Reboot_P = 2        # input  GPIO-02 (SDA with 1K8 pull-up)
System_Status_P = 3     # output GPIO-03 (SCL with 1K8 pull-up
Halt_State = 14         # output GPIO-14 (shows Halt State) not driven by this App

disable_button_action = False


# ==============================================================================
# there are a couple of directory structures used by this program
# all references to files are relative to the executing directory
# normally /home/pi
#
# set reference path to that of the location of the executed application
exec_path = os.path.abspath(os.path.dirname(__file__))

# here is where we put the shutdown flag
shutdown_file = exec_path+"/app_shutdown_ok"

# here is where we store the errors and warnings
log_file = exec_path+"/my_daemon.log"
# create log_rotate files as follows
log_rotate_file = log_file+".1"

# create the logger instance, it is used by write_log()
logger = logging.getLogger(__name__)

#===============================================================================



def init():
    '''
    Initializes a number of settings and prepares the environment
    before we start the main application.
    '''

    global sys_stat

    try:
        # Setup the logging environment
        #
        # ----- create the log file
        #
        if not os.path.isfile(log_file): # does the file exist?
            cmd = "touch "+log_file
            subprocess.call([cmd], shell=True)
            cmd = "chmod goa+w "+log_file
            subprocess.call([cmd], shell=True)
        #
        # ----- setup the main logging environment
        #
        # DEBUG is highest level => log everything
        logger.setLevel(logging.DEBUG)

        # Add the log message handler to the logger
        # you can use the following granulatity "when" to rotate:
        #    second (s)
        #    minute (m)
        #    hour (h)
        #    day (d)
        #    w0-w6 (weekday, 0=Monday)
        #    midnight
        # Here we use 7 days worth of log data
        handler = logging.handlers.TimedRotatingFileHandler(log_file,
                                                           when="h",
                                                           interval=1,
                                                           backupCount=7)
        #
        # logfiles can also be based on file size:
        # handler = logging.handlers.RotatingFileHandler(log_file,
        #                                               maxBytes=100000,
        #                                               backupCount=10,
        #                                               )

        # create the formatter, asctime is with milli seconds added
        formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')

        # add formatter to handler
        handler.setFormatter(formatter)

        # add handler to logger
        logger.addHandler(handler)
        #
        # Can now start to use write_log()
        #
        #=======================================================================
        write_log("info", "my_app_daemon V{0} initialisation started\n".format(VERSION))
        #
        # create a timestamp file to record the starting time.
        cmd = "touch {0}/STARTED_PWR_D@".format(exec_path)
        subprocess.call([cmd], shell=True)

        #
        # ----- prepare the restart of the app
        #
        # did we properly terminate the last shutdown or exit?
        if os.path.isfile(shutdown_file): # does the file exist?
            write_log("boot", "Last Shutdown was OK")
            # can restart the app with the saved data
        else:
            write_log("warning", "Last Shutdown was NOT OK")
            # need to start from scratch, saved data is not reliable
        #
        # remove the file
        cmd = "rm -f {0}".format(shutdown_file)
        subprocess.call([cmd], shell=True)
        #
        # ----- setting up the GPIO Ports
        #
        write_log("boot", "Starting hardware interface setup")

        if DEBUG: GPIO.setwarnings(True)
        else: GPIO.setwarnings(False)

        # Use the Raspberry Pi BCM pins
        GPIO.setmode(GPIO.BCM)

        # This is the port that will trigger the Power Timer to control the
        # uninterrupted shutdown period
        # Setting the Port LOW now will stop the Watchdog Timer from firing
        # until we release it again
        GPIO.setup(PWR_Timer_P, GPIO.OUT, initial=GPIO.LOW)

        # This port must now be set HIGH to turn the PowerGate on within the
        # Timer period of 45 seconds, otherwise the Pi will become powerless
        # after the Timer runs out.
        GPIO.setup(PWR_Gate_P, GPIO.OUT, initial=GPIO.HIGH)

        # Main Power loss input, we will set this up as an interrupt further below
        GPIO.setup(PWR_Sense_P, GPIO.IN)

        # system Stop_App/Pi_Reboot button, the interrupt is defined below
        GPIO.setup(PWR_Reboot_P, GPIO.IN)

        # system status LED setup
        GPIO.setup(System_Status_P, GPIO.OUT)
        # We'll use Pulse Width Modulation to control it
        # start with a frequency of 0.2 Hz
        sys_stat = GPIO.PWM(System_Status_P, 0.2)
        # start the PWM, but with a duty cycle of 0; the LED is on with no flash
        sys_stat.start(0)

        # --- these definitions need to be after the port definitions,
        # --- otherwise the detections will not work!
        #
        # setup the event to detect a falling edge on the main power sense input
        GPIO.add_event_detect(PWR_Sense_P, GPIO.FALLING, callback=power_action, bouncetime=1000)
        #
        # setup the event detection thread for the Stop_App/Pi Reboot button
        GPIO.add_event_detect(PWR_Reboot_P, GPIO.FALLING, callback=button_action, bouncetime=1000)
        #
        # if we lost the main power during the boot process, we need to
        # check this here and take action.
        if (GPIO.input(PWR_Sense_P) == 0) :
            subprocess.call(['logger "Main power lost during boot process"'], shell=True)
            write_log ("debug", "Main power lost during boot process")
            power_action(PWR_Sense_P)
            # maybe power comes back during the UPS period, otherwise we'll
            # shutdown the Pi
        #
        # start the app hart-beat
        system_status(ON, 5, 0.2) # Normal Operation, 5% ON, 95% OFF @ 0.2 Hz
        #
        write_log("system", "UPS Supply setup is finished. Starting...\n")
        subprocess.call(['logger "UPS Supply setup is finished. Starting Main"'], shell=True)

    except Exception as e:
        subprocess.call(['logger "UPS: Unexpected Exception in init()"'], shell=True)
        traceback.print_exc(file=open(exec_path+"/my_daemon_errlog.txt","a"))
        print "error in init", e
        write_log("Error", "Unexpected Exception in init() : \n{0}".format(e))

        if GPIO_cleanup :
            # reset all used I/O pins back to normal (input)
            # NOTE, this will lower the PWR_Gate and the system will be powerless
            write_log("Warning", "GPIO channels will reset, Pi will become powerless!")
            GPIO.cleanup()
        #
        os._exit(1) # force the exit to the OS, sys.exit(1) will NOT do that properly!



def write_log(mode, event):
    '''
    Function to write various messages and errors to a log file.

    We can look at the information off-line while the daemon is running.
    Use tail -f logfile

    The various modes that the logger understands:
        - DEBUG
        - INFO
        - WARNING
        - ERROR
        - CRITICAL

    Using the logger function this way allows for much more freedom on what to do
    '''

    try:
        if TRACE and mode == "trace" :
            if RUN_AS_DAEMON == False:
                # if we run the program ourselves we can print to the console
                print event
            logger.info(event)
            return

        if mode == "info" or mode == "message" :
            logger.info(event)
            return

        if DEBUG and mode == "debug" :
            logger.debug(event)
            return

        if mode == "parms" or mode == "system" or mode == "boot" or mode == "mode":
            logger.debug(event)
            return

        if mode == "error" :
            logger.error(event)
            return

        if mode == "warning" :
            logger.warning(event)
            return

        if mode == "critical" :
            logger.critical(event)

    except Exception as e:
        traceback.print_exc(file=open(exec_path+"/my_daemon_errlog.txt","a"))
        logger.error("UPS: Write_log Exception", exc_info=True)
        # do not call write_log(), we will get into a loop
        return



def set_shutdown_flag():
    '''
    Function to set a flag if we did shutdown properly.

    It allows you to properly restart your app and continue, or to really
    re-initialize your app.
    '''
    write_log("trace", "set shutdown flag")

    cmd = "touch "+shutdown_file
    subprocess.call([cmd], shell=True)



def system_status(mode, duty_c=5, freq=0.2):
    '''
    Function to drive the status LED.

    parameters : mode [ON|OFF],
                 duty_cycle [0..100] 0=on, 100=off,
                 blinking frequency in Hz
    '''
    global sys_stat

    try:
        write_log("trace", "system_status Mode={0} Duty_Cycle={1}% Freq.={2}Hz".format(mode, duty_c, freq))

        if mode:
            sys_stat.ChangeDutyCycle(duty_c)
            sys_stat.ChangeFrequency(freq)
        else:
            sys_stat.ChangeDutyCycle(100) # LED is off

    except Exception as e:
        write_log("error", "system_status Exception : \n{0}".format(e))
        logger.error("UPS: System_status Exception", exc_info=True)
        return



def button_action(PWR_Reboot_P):
    '''
    This call_back function controls the pressing of the Reboot/Terminate button.

    The debounce does not always work, so we do it in software.
    The call_back function should not call itself again while we're processing
    the button press, so we use a traffic light.

    '''
    global disable_button_action

    if disable_button_action :
        # already processing...
        return
    else:
        disable_button_action = True
        process_button(PWR_Reboot_P)



def process_button(PWR_Reboot_P):
    '''
    This function controls the pressing of the Reboot/Terminate button.
    Based on a software timer, we either Reboot or terminate this program.

    When we reboot after the shutdown, a filecheck is performed.
    '''
    global sys_stat, disable_button_action

    try:
        write_log("warning", "Reboot/Terminate Action Detected")

        button_press_timer = 0

        # signal user that we've seen the button press
        # starting to flash the LED
        system_status(ON, 50, 1)

        while (GPIO.input(PWR_Reboot_P) == False) :
                write_log("debug", "reboot_button_action : Timer={0} Sec".format(button_press_timer))

                if button_press_timer > 15 :
                    # second thought or something went wrong
                    break

                if button_press_timer == 8 :
                    # Threshold for Halting program is reached
                    # flash at 10 Hz so the user can release if the Halt is wanted
                    system_status(ON, 50, 10)

                elif button_press_timer == 4 :
                    # Threshold for the Reboot
                    # flash at 4 Hz so the user can release if the Reboot is wanted
                    system_status(ON, 50, 4)

                button_press_timer += 1 # keep counting until button is released
                sleep(1) # 1 sec timer for button_press measurement

        # The button is released, figure out for how long
        write_log("debug", "button released : Timer={0} Sec".format(button_press_timer))

        # is press is <= 4 or > 15 : no action
        if (button_press_timer <= 4) or (button_press_timer > 15):
            write_log("trace", "Reboot button pressed, but too short/long")
            button_press_timer = 0
            system_status(ON, 5, 0.2) # system LED flashing back to normal
            disable_button_action = False
            return

        # is press for > 4 < 8 seconds : Reboot!
        if (button_press_timer > 4) and (button_press_timer < 8):
            write_log("trace", "System reboot by reset button")
            #
            #
            if REBOOT :
                # Start the Timer and release the Watchdog
                GPIO.output(PWR_Timer_P, GPIO.HIGH)
                sleep(0.01)
                GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
                sleep(0.1)
                GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
                # Don't release the power gate of the UPS to keep power on
                # this shutdown is actually a reboot and we do a file check as well
                subprocess.call(['shutdown -F -r now "System reboot by reset button" &'], shell=True)
                sleep(30) # prevent the system from doing things
            else:
                write_log("debug", 'shutdown -F -r now "System reboot by reset button"')
                system_status(ON, 5, 0.2) # system LED flashing back to normal
                disable_button_action = False
                write_log("debug", "REBOOT = False : returning to main")
            return

        if (button_press_timer >= 8) : # pressed for > 8 seconds : Terminate!
            write_log("trace", "Program will be terminated by reset button")
            #
            # Start the Timer and release the Watchdog
            GPIO.output(PWR_Timer_P, GPIO.HIGH)
            sleep(0.01)
            GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
            sleep(0.1)
            GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
            #
            # terminate the program
            write_log("trace", "Terminating Program")
            # force the exit to the OS
            if TERMINATE_2_OS :
                os._exit(0)
            else:
                write_log("debug", "TERMINATE_2_OS is False : going back to main")
                system_status(ON, 5, 0.2) # system LED flashing back to normal
                return

    except Exception as e:
        write_log("error", "Unexpected Exception in reset_action() : \n{0}".format(e))
        subprocess.call(['logger "UPS: Unexpected Exception in reset_action()"'], shell=True)
        disable_button_action = False
        return



def power_action(PWR_Sense_P):
    '''
    This function controls the Power to the Pi.

    If a loss of main power is detected, the supply acts as a UPS to see if it
    only was a glitch, if not, the Pi is shutdown.

    After the shutdown of the Pi, it will become powerless. The power supply
    will automatically turn the power on to let the Pi boot if the main
    power returns.
    '''
    try:
        write_log("warning", "power action -> Loss of Power Detected")

        # flash at 4 Hz so the user can see we have a loss of main power
        system_status(ON, 50, 3)

        sleep(1) # Let the voltage drop completely
        # we can play UPS for a while before we perform the shutdown.
        # if there was a power glitch, we'll let the Pi continue
        # if the mains is really out and we lost power, we need to shutdown the Pi.

        ups_timer = 1
        while (ups_timer < UPS_MODE) and (GPIO.input(PWR_Sense_P) == 0): # play UPS
            sleep(1) # check every second
            ups_timer += 1
        write_log("trace", "UPS mode finished : UPS={0} timer={1}".format(UPS_MODE, ups_timer))

        if ups_timer < UPS_MODE :
            # The power came back!
            sleep(1) # let the system settle a bit and check it again
            if GPIO.input(PWR_Sense_P) == 1 :
                write_log("trace", "Power came back on during UPS period")
                system_status(ON, 5, 0.2) # system LED flashing back to normal
                return

        write_log("trace", "Main power still off -> starting shutdown process")
        # show the user
        system_status(ON, 50, 6) # start flashing at 6 Hz
        #
        # start the shutdown process
        #
        # Start the shutdown process
        if SHUTDOWN:
            write_log("trace", "Start shutdown sequence!")
            #
            # Start the Timer and release the Watchdog
            GPIO.output(PWR_Timer_P, GPIO.HIGH)
            sleep(0.01)
            GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
            sleep(0.1)
            GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
            #
            # Now release the power gate of the UPS to let the 555 timer
            # control the power to the Pi until shutdown is completed.
            write_log("trace", "Release power gate and start shutdown sequence")
            GPIO.output(PWR_Gate_P, GPIO.LOW)
            #
            subprocess.call(['shutdown -hP now "System shutdown due to main power failure" &'], shell=True)
            # when the shutdown sequence has started, it cannot be stopped. If the power comes back on
            # in this period, the Pi will still be left powerless. In that case,
            # the PWR_Watchdog will restart the Pi.
            #
            sleep(60) # don't return, wait for the shutdown to happen
        else:
            write_log("trace", "SHUTDOWN = False: Start test of shutdown sequence")
            write_log("debug", 'shutdown -hP now "System shutdown due to main power failure" &')

            if GPIO_cleanup :
                # reset all used I/O pins back to normal (input)
                # NOTE, this will lower the PWR_Gate and the system will become powerless
                write_log("warning", "GPIO channels will reset, Pi will become powerless!")
                GPIO.cleanup()
            write_log("debug", "return to main")
            system_status(ON, 5, 0.2) # system LED flashing back to normal
            return

    except Exception as e:
        # in case that there is an exception, still force the shutdown!
        # if the shutdown flag has already been set, fine, otherwise, we'll have
        # to restart from scratch
        #
        write_log("error", "Unexpected Exception in power_action() : {0}, reboot".format(e))
        subprocess.call(['logger "UPS: Unexpected Exception in power_action(), reboot"'], shell=True)
        sleep(2)
        if SHUTDOWN :
            # Make sure we can finish the reboot without a power interruption
            # Start the Timer and release the Watchdog
            GPIO.output(PWR_Timer_P, GPIO.HIGH)
            sleep(0.01)
            GPIO.output(PWR_Timer_P, GPIO.LOW) # start the Timer
            sleep(0.1)
            GPIO.output(PWR_Timer_P, GPIO.HIGH) # release the Watchdog
            #
            # Now release the power gate of the UPS to let the 555 timer
            # control the power to the Pi until shutdown is completed.
            write_log("trace", "Release power gate and start shutdown sequence")
            GPIO.output(PWR_Gate_P, GPIO.LOW)

            if GPIO_cleanup :
                # reset all used I/O pins back to normal (input)
                # NOTE, this will lower the PWR_Gate and the system will become powerless
                write_log("warning", "GPIO channels will reset, Pi will become powerless!")
                GPIO.cleanup()

            subprocess.call(['shutdown -F -r now "System reboot due to error in power_action()" &'], shell=True)
            sleep(60)
        else:
            write_log("debug", 'shutdown -F -r now "System reboot due to error in power_action()" &')
        return



def sig_handler (signum=None, frame=None):
    '''
    This function will catch the most important system signals, but NOT a kill termination!

    This allows us to catch these events and do a gracefull termination of our application.

    This handler catches the following signals from the OS:
        SIGHUB = (1) SSH Terminal logout
        SIGINT = (2) Ctrl-C
        SIGQUIT = (3) ctrl-\
        IOerror = (5) when terminating the SSH connection (input/output error)
        SIGTERM = (15) Deamon terminate (deamon --stop): is coming from deamon manager
    However, it cannot catch SIGKILL = (9), the kill -9 used in the kernel shutdown
    process.

    NOTE: if the termination takes longer than the standard start-stop daemon allows,
    modify : start-stop-daemon --stop --pidfile $PIDFILE --retry=TERM/10/KILL/5
    send TERM with a longer wait and then KILL with 5 seconds
    '''
    try:
        write_log("trace", "Sig_handler called with signal : {0}".format(signum))
        if signum == 1 :
            write_log("trace", "ignoring signal {0}".format(signum))
            return # 1:ignore SSH logout termination
        write_log("trace", "Sighandler is terminating the application")
        # start flashing the status LED so we will see what's going on
        system_status(ON, 50, 10)
        #
        # add code here to do a graceful termination, and to allow for a clean
        # reboot
        #
        # set the flag to show that we exited gracefully
        set_shutdown_flag()
        #
        if GPIO_cleanup :
            # if we do a GPIO.cleanup, the Pi will become powerless right away!
            write_log("trace", "GPIO channels will reset, Pi will become powerless!")
            GPIO.cleanup()
        #
        write_log("trace", "Sig Handler is done, Exiting to shell\n\n")
        sleep(2)
        os._exit(1) # force the exit to the OS

    except Exception as e: # IOerror 005 when terminating the SSH connection
        write_log("error", "Unexpected Exception in sig_handler() : \n{0}".format(e))
        subprocess.call(['logger "UPS: Unexpected Exception in sig_handler()"'], shell=True)
        return



def main():
    '''
    The main daemon routine.

    After the initialisation, we start the control thread.

    We try to catch the most important os signals so we can terminate the program but preserve the integrity.
    Note: we cannot catch kill -9 or shutdown, so that will be handled in the deamon manager that starts
    this program at boot time.

    '''
    #
    init()

    write_log("trace", "Run as Daemon = {0}".format(RUN_AS_DAEMON))
    write_log("trace", "GPIO Version = {0}".format(GPIO.VERSION))
    write_log("trace", "Trace = {0}".format(TRACE))
    write_log("trace", "Debug = {0}".format(DEBUG))
    write_log("trace", "GPIO_cleanup = {0}".format(GPIO_cleanup))
    write_log("trace", "Reboot = {0}".format(REBOOT))
    write_log("trace", "Shutdown = {0}".format(SHUTDOWN))

    # setup a catch for the following signals: signal.SIGINT = ctrl-c
    for sig in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT):
        signal.signal(sig, sig_handler)


    try:

        while True :
            sleep(1)
            #
            # wait for a button press to terminate or shutdown
            #
            # watch out for a loss of the main power

    except Exception as e:
        write_log("error", "Python exception : {0}".format(e))
        # capture the traceback information causing the Python termination
        #
        # add code here to handle a restart
        #
        # set a flag if we exited normally
        set_shutdown_flag()
        # so we can restart the program again
        #
        # Make sure we can terminate properly
        # Release the Watch_Dog
        GPIO.output(PWR_Timer_P, GPIO.HIGH)

        if GPIO_cleanup :
            # reset all used I/O pins back to normal (input)
            # NOTE, this will lower the PWR_Gate and the system will become powerless
            write_log ("warning", "GPIO channels will reset, Pi will become powerless!")
            GPIO.cleanup()
        #
        subprocess.call(['logger "Forcefully terminating with error"'], shell=True)
        write_log("error", "UPS: Forcefully terminating with error {0}".format(e))
        # exit en return an error code for this instance
        os._exit(1) # force the exit to the OS


if __name__ == '__main__':
    main()
Because the program runs as a daemon, you’ll need to direct all logging activities to a file. The activity of what gets written into this file can be looked at while it’s running with the command:

Code: Select all

tail –f *your_log_file.log
I have added code to do the logging and also added some bells and whistles to add more features and flexibility. It is explained in the source.
Your application needs to handle sudden shutdowns, which is why I added a function to catch all attempts by you or the Pi to terminate the daemon. You can then do what you need to save critical data and continue where you left off when the Pi is rebooted again.


Testing
During the testing of the hardware, I recommend that you do the following.

At the earliest opportunity, even before embedding the converter in your circuit, set the output of the DC-DC convertor to 5V!
Use the batteries to feed the converter if needed.

Build the hardware and make it complete. Connect the GPIO connections between the supply and the Pi, but do not supply the power to the Pi just yet. Use your regular supply to start the Pi, and this allows you to test the power supply without causing any harm.
I have added several Global variables in the software that allow you to disable reboots and shutdowns while you are testing. You can also turn on the maximum amount of debugging and tracing so you can see what is going on within the app. After you are satisfied, you can connect the Pi to the power supply and first tune the 5V supply by setting it to 5.0V while you measure that between TP-1 and TP-2 on the Pi board.

Then try the following tests.
  • 1. Power Failure During Normal Operation
    Turn the 12V on and then switch on the battery supply. (do not switch it off again until all tests are finished!)
    Let the Pi boot. The status LED will start to flash as soon as the program has control.
    First test is to turn off the 12V supply.
    The LED will start to flash faster during the UPS phase, and will flash even faster during the shutdown phase.
    The Pi will Halt, as you can see from the Halt LED, or from the 10 flashes of the activity LED on the Pi board.
    Shortly after, the power will be cut.
    2. Power failure during Boot.
    Turn on the 12V supply again, and the Pi should boot again.
    While it is booting, and while the Status LED is dimly lit, turn the 12 V power back off.
    The Pi will finish the boot, will initialize the program, will notice that the main supply is not there, will go through the UPS phase and then shutdown immediately.
    3. Power failure during Shutdown
    Turn the 12V back on.
    Now let the program take control of the Pi, so wait until the Status LED starts to flash slowly.
    Turn the 12V off, and wait until after the UPS phase, when the status LED will start to flash even faster.
    Turn the 12 V back on.
    The Pi will shutdown, and power will be shut off. (this may take a little, be patient)
    After a little while, the Watch-Dog will turn the power back on again, and the booting process starts again.
Make sure you test these three tests and that they work. Only then install the program as a daemon. You can also change some of the Debugging and Test Global variables if you want.

Helpers
If you create four shell files like this one, call them start, stop, status and restart and it will be easier to start, restart, stop, or get the status of the daemon. Replace the key word start below with the four variations:

Code: Select all

#!/bin/sh
sudo /etc/init.d/my_ps_service.sh start
Make them executable:

Code: Select all

 chmod +x start 
Call them with

Code: Select all

./start
Operating this thing
So how do we work with this supply? Well start so everything is powerless. That’s why you need the switch in the battery supply. Now add the 12V, and then switch the battery supply on, and the rest is history as they say. You will now have a completely automatic supply. If you run the Pi in an embedded application, that’s it for you. If you have a desk-top application that needs to run 24x7 (like a server etc.), consider that an embedded application.

If you run the Pi in a “desk-top” situation, and you want to turn it off completely, you can finally yank the 12 Volt supply cable or wall-wart with immunity and after the Pi has become powerless, you can switch off the batteries. If that is your application, and you regularly want to turn it on/off, I would add a switch for the 12V supply at the mains (110-230V AC) input end.

Your Program Exceeds the approx. 50 Second termination time
If your application needs to save data or does otherwise need time before it can safely terminate (or gets terminated), you need to two things.
1. Extend the time between the Signal -15 terminate and the kill -9 signals in the my_ps_service.sh script.
2a. Extend the Timer period by changing R15 to a higher value.
2b. Or, you can use the TXD output at GPIO-14 and connect that with a diode to the Power-Switch input (Gate of Q2), and so extend the OR we already have. In that case, the power will stay on, even after the Timer has finished, and it will now be turned off as soon as the Pi has Halted. The data transmission on this port during the boot period does not hinder this setup.

Using different main supply voltages:
5V :
R4 = 2K2, R6 = 0Ohm, D3 is 1N5819, R8 is a 1 Ohm resistor 1/4W, no R9, no D2, you can only use 3 cells!
15V:
R4 = 15K, R6 = 22K, R8 and R9 = 160 Ohm 1W each, if possible use a higher wattage or use more in parallel.
24V:
R4 = 20K, R6 = 39K, R8 and R9 = 360 Ohm 2W each, if possible use a higher wattage or use more resistors in parallel.

As I mentioned in the section where we discuss the power suppy part, if you have a walt-wart that supplies more than 24V, you need to add a circuit to bring the higher voltage down to 24V. The DC-DC up-/down convertor we use cannot handle voltages over 28V, that's why.

Using higher currents:
Change F1 and F2 to 110% of the Amps value.
For 2 Amps use passive cooling for the two chips on the DC-DC convertor like I use in the picture.
for 3 Amps, which is the maximum, use bigger cooling fins or a little fan.
D5 and D6 should be changed to 3Amp 1N5822.
If you use a different P-channel MOSFET than the one I use (Si3443DV), make sure it can handle the higher current.
Make sure that all the wiring and connectors in the current path can carry these higher currents.
You may have to also revisit the length of time your batteries can supply this current in the UPS phase without depleting them too much.

Have fun!


1 comment:

Joseph Mikel said...
This comment has been removed by a blog administrator.