paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:18 pm

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
backup.png (4.86 KiB) Viewed 33157 times
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
charger.png (7.06 KiB) Viewed 33157 times
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.

Up to part 2

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:26 pm

Part 2

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
DC-DC convertor.jpg (32.74 KiB) Viewed 33147 times
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
power switch.png (2.58 KiB) Viewed 33147 times
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
DIP Adapters.jpg (41.09 KiB) Viewed 33147 times
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.

Up to part 3
Last edited by paulv on Sun Dec 20, 2015 7:59 am, edited 3 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:35 pm

Part 3

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
mains detection-1a.png (6.41 KiB) Viewed 32504 times
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
Timers-1a.png (16.03 KiB) Viewed 32504 times
(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
Connector.jpg (31.43 KiB) Viewed 33131 times
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/

Up to part 4
Last edited by paulv on Sun Dec 20, 2015 8:37 am, edited 8 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:38 pm

Part 4

Here is the schematic of the complete solution:
Automatic PS.png
Complete Schematic
Automatic PS.png (59.75 KiB) Viewed 32282 times
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
ps pcb.JPG (40.56 KiB) Viewed 33131 times
And the Pi top board:
Pi GPIO Board.jpg
Pi Top Board
Pi GPIO Board.jpg (16.79 KiB) Viewed 33131 times

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.

Up to part 5
Last edited by paulv on Sun Dec 20, 2015 8:38 am, edited 4 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:42 pm

Part 5

Here is an earlier prototype where I’m using my big lab DC Power Supply to feed the 12V, so I can run tests, but in reality, another barrel connector feeds the 12V DC into the supply. The batteries are on the left of this hand-held enclosure, which is great for desktop use.
ps complete.JPG
PS complete
ps complete.JPG (40.25 KiB) Viewed 33129 times
One of the interesting things I found by using the DC-DC step-up / -down convertor is that if you switch on the input (with 12V), the 5V at the output is there earlier. Have a look at this scope screenshot:
Power Startup.jpg
Supply Startup
Power Startup.jpg (43.08 KiB) Viewed 33129 times
The top trace (A) is that of the 12V input, below is the 5V at the output of the DC-DC convertor.

The reason we already have 5V at the output before we have the full 12V is because of the step-up (boost) function. It starts to produce 5V at the output at an input voltage of about 2.8V already. This is a great feature, and it ensures that we have a clean 5V for our logic and at the Power-Switch before we turn the Pi on. Remember, we do that through the 12V sense input and then trigger the Watch-Dog with it. It in turns fires the Timer, which turns on the Power-Switch. This sequence and a little delay will ensure that we have a clean and stable supply to boot the Pi with.

Here is a screenshot that shows that it takes about 35 seconds from power on (trace A is the 12V supply) before our controlling program is active (trace B going low):
Boot Period.jpg
Boot Period
Boot Period.jpg (21.89 KiB) Viewed 33129 times
Remember that this is depending on a number of things that take place during the booting of the Pi. Our Timer-Window is set for about 45 seconds and that should provide sufficient time to turn the Power-Switch on by our program before it runs out and switches the power off.

Up to part 6
Last edited by paulv on Sun Dec 20, 2015 8:42 am, edited 3 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:46 pm

Part 6

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
Up to part 7
Last edited by paulv on Sat Jan 09, 2016 9:12 am, edited 3 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:48 pm

Part 7
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
Power_Timer & Watch_Dog.jpg (43.26 KiB) Viewed 32132 times
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.

Up to part 8
Last edited by paulv on Sun Mar 08, 2015 3:05 pm, edited 6 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 12:51 pm

Part 8

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}/[email protected]".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.

Up to the last part, 9
Last edited by paulv on Fri Mar 13, 2015 11:52 am, edited 6 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 1:08 pm

Part 9

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!
Last edited by paulv on Sat Jan 09, 2016 9:16 am, edited 5 times in total.

scotty101
Posts: 3648
Joined: Fri Jun 08, 2012 6:03 pm

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 1:15 pm

Wow!
Great project and very well documented. Thanks for posting.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

stortryne
Posts: 11
Joined: Wed Feb 11, 2015 3:14 pm

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 2:46 pm

May I suggest not using NiCd-batteries for this build? When only partially discharged and recharged, they will wear out rather quickly. I would prefer using Pb-batteries. About as hi-tec as a brick, but simple and reliable :)

User avatar
SteveDee
Posts: 343
Joined: Thu Dec 29, 2011 2:18 pm
Location: Sunny Southern England
Contact: Website

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 2:59 pm

Good quality information once again Paul. Many thanks.

I just wonder if this is too good for this forum (as a post, I mean). It should be in a Pi wiki, or maybe published in MagPi.

User avatar
morphy_richards
Posts: 1603
Joined: Mon Mar 05, 2012 3:26 pm
Location: Epping Forest
Contact: Website

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 3:46 pm

I just wonder if this is too good for this forum (as a post, I mean). It should be in a Pi wiki, or maybe published in MagPi.
I would second that, erm I believe the phrase that is "down" with the younger generation these days is "plus one".

It would be great to read up in the MagPi. I for one will definitely need this in the future but I worry that I might not find it again x months / years down the line.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 4:37 pm

Edited on 2-March-2015

Another Option
Here are two refinements for the more inclined and capable DIY'er if you want to have more freedom in selecting the wall-wart supplies.
With these changes the supply can use anything between 10 and 24V DC at the input.

There are two areas that need a tune-up to make the Pi supply use that range of voltages.
The charger needs to be modified:
Charger-3.png
Modified Charger Circuit
Charger-3.png (8.71 KiB) Viewed 32699 times
An LM317 linear voltage regulator is used in the constant current mode to supply the charging current. Two can be used in parallel to counter the heat that they develop. This depends on the size of the heat-sink you can accommodate in your packaging. The LM317 costs less then 1$, a price I would gladly pay to reduce the more expensive heat-sink that is otherwise required.
The use of the constant current devices eliminates the selection of various resistor values to set the charging current. With a 100mA trickle-charge, the LM317 gets hot and does need cooling. Even in the parallel mode, I suggest you use some form of passive cooling. The formula to calculate the resistor is:
Iout = Vref (1.25V) / R
With our 100mA charge current, the calculated value is 12.5Ohm. 1.25V * 100mA = 1/8W. With two devices in parallel, the resistor should be double the calculated value, so this would be 27Ohm for a current of 46mA each; close enough.

I am no expert on battery cells, but I wonder myself if the recommend maintenance charge factor between 0,03 and 0,05 resulting in our 100mA for 2200mAH capacity cells is really needed in true embedded applications where long brown-outs are rare (depending where you live of course), and the Pi is not switched on and off a lot. Charging at a rate of C/10 is the recommended minimum charge to get the full capacity stored in the cells, but you can't do that for long periods. C/20 is the recommended trickle charge that can be used to maintain the full charge. In our case however, we fix the charge rate to a level even slightly below the trickle charge rate (C/22), and we never fully charge at C/10. How much real capacity we can get out of the cells is depending on many variables, but it could be as low as 60-80% or even less. So, be aware of this and plan for an available capacity of 50% to be safe, and work out your UPS phase timing from this number.
If you are using this supply for a desk-top or Media-player kind of solution, with relatively often power-up and power-down actions, or when you use lower capacity cells, you may have to increase the charge rate of the circuit.

The minimum voltage limit of the supply is determined by the required voltage drop over the LM317. It cannot supply a constant current below 8-9V, so that sets the lower limit at 10V.
The maximum input voltage of 24V is still limited by the DC-DC converter, unless you use another voltage regulator to drop the excess voltage down to that level.

The main voltage detection circuit needs to be changed too.
mains detection-2.png
Enhanced Mains Detection
mains detection-2.png (6.3 KiB) Viewed 32282 times
To make the sensing inputs independent of the main voltage supply, we will use a Zener diode that will clamp the voltage level.
Because Zener diodes need to be biased with approx. 5mA, the higher current increases the potential for a disaster when you make a mistake. This is why I did not use this in the "safer" version.

Note that I also added an extra diode at the input voltage input to protect for voltage reversal which is more likely when you don't use a dedicated wall-wart. (some have the plus at the center pin, some have ground there)

If you want to be extra careful with your Pi, you can leave in the protection diode for the GPIO port to 3V3. It can handle more current than the 5V6 clamping circuit we expect to have on the die of the SOC itself.

Here is the full schematic of the enhanced version:
Enhanced Version.png
Enhanced Version
Enhanced Version.png (61.27 KiB) Viewed 32282 times
Enjoy!

Paul
Last edited by paulv on Thu Dec 24, 2015 4:43 am, edited 13 times in total.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Fri Feb 27, 2015 5:25 pm

stortryne,
Good suggestion about the batteries.
I don't think however that in normal situations, in embedded applications, the batteries will be used much.
They are an insurance policy if the main power drops, and otherwise only used when you shut the main power off, to make the Pi powerless. This will take about 1 minute each time.

I'm not enough of an expert to know about the lifetime effects of this so users doing regular startup and shutdown's several times per day should take notice, or be prepared to replace the cells when they are going bad.

Thanks,
Paul

hampi
Posts: 223
Joined: Fri May 31, 2013 11:29 am
Contact: Website

Re: HOW-TO Build an Automatic Power Supply for the Pi

Sat Feb 28, 2015 9:19 am

You have really put some effort on that one and written long documentation. I need to read it again more carefully.

Some quick notes from me.

The switching mode regulators like LM2576 and others usually have a shutdown pin, which can be used stop and restart the 5 V power supply for RPi.

For testing I would recommend loading the supply with a power resistor of about 2 ohms (about 2.5 A current) for several hours. If the supply survives that test it will work fine with RPi.

The double 555 timer is something similar to my PiPIC done with PIC12f675 and written is plain assembly code (writing/debugging took some time - yet not finished, but somehow working):

https://github.com/oh7bf/PiPIC/wiki

It allows two more general timed tasks to be active at the same time.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Sat Feb 28, 2015 2:09 pm

The switching mode regulators like LM2576 and others usually have a shutdown pin, which can be used stop and restart the 5 V power supply for RPi.
Good idea, I looked at that too, unfortunately, the LM2576 does have an ON/OFF input, but the LM2577 does not. Besides, prying a pin lose from an SMD package and soldering a wire to it is not intended for the audience I targeted my project too. (look at my objectives)
For testing I would recommend loading the supply with a power resistor of about 2 ohms (about 2.5 A current) for several hours. If the supply survives that test it will work fine with RPi.
Yes, that is what I did, but if the design is followed, you are really only testing the DC-DC convertor with this "stress" test. They work or don't, so I think this is not really needed.
The double 555 timer is something similar to my PiPIC done with PIC12f675 and written is plain assembly code (writing/debugging took some time - yet not finished, but somehow working):
Well done, I like your flow chart, but remember that solutions like these went against my objectives. The goal was to avoid using a single chip processor with all pitfalls and difficulties.

https://github.com/oh7bf/PiPIC/wiki

It allows two more general timed tasks to be active at the same time.[/quote]
Last edited by paulv on Wed May 20, 2015 8:11 pm, edited 1 time in total.

grahamed
Posts: 277
Joined: Mon Jan 30, 2012 7:01 pm

Re: HOW-TO Build an Automatic Power Supply for the Pi

Sat Feb 28, 2015 4:11 pm

Hi

Very nice.

A bit back I made a PSU/control unit myself. I seem to have lost most of the work but maybe this bit would be of interest to someone.
Clipboard03.jpg
Clipboard03.jpg (31.12 KiB) Viewed 32654 times
The pic shows a perfect diode at the top, and a perfect diode with disable at the bottom. The 5V input, if present, will pass straight through the top diode, will charge the battery (not shown but between the modules) and will disable the lower diode/switch via the zener.

If would be possible to use just the diode/switch if required - remove the pot/resistors from the lower RHS npn base and drive it high or low.

The perfect diode eliminates the volts drop associated with a diode and the switch prevents the battery derived 5V passing if the mains 5V is available; if there were two perfect diodes whichever 5V was higher would pass (OK the body diode will conduct at plus 0V7 or so) , and if the diode/switch were a just a switch the mains derived 5V might find its way back to the boost module output.

If any one wants I can post the spice model - if and when I find it.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Mon Mar 02, 2015 9:27 pm

I have made some changes to the text, updated a few circuit diagrams and added one more screenshot to the original posts. This keeps it together for people that will find it later on, but if you already did, you may want to have a look again.
The most significant change was about a further automation element for the batteries. They are now also removed from the circuit automatically, making this a truly automatic Power Supply for the Pi, and even more useful for desktop applications. It is now completely plug&play&pull ;)

I hope you''l like it.
Paul

Joe Schmoe
Posts: 4277
Joined: Sun Jan 15, 2012 1:11 pm

Re: HOW-TO Build an Automatic Power Supply for the Pi

Mon Mar 02, 2015 10:22 pm

Are there any plans to produce this - either as a kit or as a finished product?
And some folks need to stop being fanboys and see the forest behind the trees.

(One of the best lines I've seen on this board lately)

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Tue Mar 03, 2015 3:53 am

Joe Schmoe wrote:Are there any plans to produce this - either as a kit or as a finished product?
Not by me Joe. Hopefully somebody that knows how to do this wil be interested, and publish the result.

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Wed Mar 04, 2015 8:44 pm

Here are the final shots of the completed package:
final build.jpg
Final build
final build.jpg (41.03 KiB) Viewed 32279 times
And the circuit board:
PS final build.jpg
Final Build PS board
PS final build.jpg (45.65 KiB) Viewed 32279 times
I left room for enhancements, maybe I'll add the circuit that measures the battery voltage so I can turn the Pi off if they get depleted too much. All the way up top in the second post I've made a link to a version that already had that.

Enjoy,

Paul

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Sun Mar 08, 2015 4:19 pm

While going through some more testing and real life situations, I discovered a potential problem with the charger circuit (with the resistors or the LM317 version).
Charger-3.png
Original Charger Circuit
Charger-3.png (8.71 KiB) Viewed 32132 times
If you remove one or all of the batteries, the output of the charging circuit will be pulled to almost the main supply level. The maximum charging voltage Zener diode of 5V6 will take the full hit to bleed that off, and will get (smoking) hot. This is an obvious side-effect of this simple circuit.

So, you either need to make sure that the batteries will not be removed, or you can use the following circuit to avoid that possibility all together.
Charger-4.png
Enhanced Charger Circuit
Charger-4.png (13.94 KiB) Viewed 31988 times
(the switch S1 is really a silicon Power-Switch, here to make the circuit simple)

Because I used a cooling strip (see the picture of my power supply above) that was large enough to absorb the temperature of a single LM317 in the constant current mode, I used the second one to provide a constant voltage circuit. With the resistor values I show, the first LM317 delivers a maximum of 7.2V to the second LM317 in the constant current mode. With the approx. 1.7V voltage drop over that LM317, we arrive at a safe charging voltage of about 5.5V. (depending on the accuracy of the R1 and R2 values) D7 protects the LM317 chain from reverse voltages if there is no main power.

Provided the resulting voltage is less than that of the 5V6 Zener, it is no longer needed, but I left it in as a safety precaution for the batteries.

A positive side-effect of this new circuit is that the main power signal can now be tapped from the 7.2V output of the first LM317, and you can avoid the high voltage issues for the GPIO input. This will also eliminate the need for a voltage reversal protection diode (D1 in the original circuit). Because D3 and D5 will take care of that.

Have fun!

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Wed May 20, 2015 8:08 pm

I was not so happy with the heat developed in the DC-DC convertor module, so I investigated that device a bit more.

In an earlier post I mentioned that I discovered that there are at least two versions of the DC-DC Buck-Boost convertors on the market. One is indeed based on the LM2577 & LM2596 combo, but there is one version that is based on the XL6009 & LM2596 combo. The XL6009 is advertised as an improvement of the LM2577, and will deliver a current of 4A instead of the 3A for the 2577. The TO-263 packages that are used do NOT have the same pin-out, but since we use them on a PCB already that does not concern us.

Another difference between the two is that the ON/OFF input for the 2577 is active low, and the ENABLE pin for the XL6009 is active high, again this is only of interest if you want to switch off the output of the convertor.

I tested both versions and the good news is that there is really no difference from a black-box perspective. Both already start working at an input voltage of just below 3V, which is great.

Reducing the temperature
What is an inherent weakness of this design is that a lot of energy is wasted. The board works as follows. The first stage that uses the LM2577 or the XL6009 are both programmed to output 31.7V, regardless of the input of course. This output voltage is then reduced by the LM2596 convertor, controlled by the trimmer. This is done so you can adjust the output from 30V downwards. In our case, supplying only 5V to the Pi, a lot of energy is used in this boost-buck process. As a matter of fact, applying 4V to the input, and with the output set at 5V, there is a 100mA quiescent current flowing, without any load present.

Luckily there is an easy fix. The voltage divider that sets the Feed-Back for the first boost stage is made of a 20K resistor from the output connected to a grounded 820R resistor. The Vout formula is 1.25 X (1 + 20.000 / 820) = 31.7V. The easiest way to reduced that is to use a normal 1/8W 4K7 resistor in parallel to the 20K SMD chip, by connecting this resistor between the output of the LM2577 or XL6009 (at the + of the 330uF capacitor) and the FB pin. 4K7 // 20K gives 3K80 and using that in the above formula results in 7V. This should give enough headroom to produce 5.1 or 5.2V at the output of the board, and to feed that to the Pi. With that resistor in place, the quiescent current drops to 2mA, which is quite an improvement.

Keep in mind though that when you feed in a voltage above the 7V, (12 up to 24V) that higher voltage will be at the output of the LM2577 or the XL6009. Nevertheless, depending on your input voltage, it will be less than the original 31.7V, and still reduce the wasted energy.

I hope this helps to reduce wasted energy and to keep your unit cooler.

Enjoy!
paulv

paulv
Posts: 558
Joined: Tue Jan 15, 2013 12:10 pm
Location: Netherlands

Re: HOW-TO Build an Automatic Power Supply for the Pi

Mon Nov 30, 2015 10:15 am

After having used this supply with my file server for several months, I decided to update/upgrade the firmware to move to Jessie.
Unfortunately, the system stopped working. During the boot, the power was cut by the UPS system.

It turns out that the boot time with the latest kernel code is longer than before. Now, I also run a file check at boot time, because I put forcefck into the /boot/cmdline.txt, so the boot time of my server is already longer. (using a slower classic B Pi also does not help)

Remember that the UPS program needs to set the PWR_Gate input to the UPS high, to continue to have power to the Pi. The additional boot time caused the UPS program to load later, causing the power-window timer to run out, and cutting out the power.

This was a potential problem in the waiting, because when a filesystem problem has been found during the file check, and it starts a repair process, it would have caused the same problem anyhow.

There are two solutions. One is to lenghten the time of the 555 timer that controls the boot window. And another one is to set the PWR_Gate pin (GPIO-17) high earlier in the process, or better yet, make it independent of the boot period all together. This is the safest method and what I wanted to do. The reason is that if there is a problem with the file check during the boot period, and a repair action is started, the boot process can take a very long time.

You can rather easily accomplish this by using the device tree method. And again we have two possibilities. In simple terms, you can modify the standard blob.dts file, and that will set the pins right at power-on, or you can create your own, dedicated blob.dts file as an overlay, and that gets loaded after about 3.6 seconds into the boot process. The file check takes place after that, so that option is possible too.

I selected to modify the blob.dts file that gets loaded at power-up by the video core. This file is a representation of what is hard coded into the microcode of the Pi, and you can use this file to change that behavior.

There are several posts on the Forum on how to do it. Here is what I did in short:
Download the latest blob.dts file from the Forum.
https://www.raspberrypi.org/documentati ... t-blob.dts
Copy it and call it my-ups-blob.dts
Edit this file:

Code: Select all

nano my-ups-blob.dts
In the file, locate all the "}; // pin" sections (there is one for every Pi model) and add the following into each section. (no need to do the Compute Model)

Code: Select all

            }; // pin
            //
            // setting the GPIO's for the automatic UPS supply
            //
            [email protected]  { function = "input";  termination = "no_pulling"; }; // PWR_Sense
            [email protected] { function = "output"; termination = "pull_up"; polarity = "active_low"; startup_state = "active"; }; // PWR_Gate
            [email protected] { function = "output"; termination = "pull_down"; polarity = "active_high"; startup_state = "active"; }; // PWR_Timer
            //
            //
I also added the definitions for the other two GPIO pins, so I won't get another suprise when the pins get changed once more at a new kernel revision. (it happened before!)

Note that in the first section, for the "pins_rev1", you also need to comment out the definition for port 27!

Code: Select all

//            [email protected] { function = "output"; termination = "no_pulling";    }; // Camera shutdown
Save your file.

You may still need to install the device_tree compiler :

Code: Select all

sudo apt-get install device-tree-compiler
Compile the code:

Code: Select all

sudo dtc -I dts -O dtb -o /boot/dt-blob.bin my-ups-blob.dts
This will place the binary directly in the /boot directory.
If a file with this name (dt-blob.bin) is in /boot, it will automatically get loaded at boot time.
Reboot.

( if you have not done this before, and want to be safe, do all of the above, but do not make any changes to the dts file yet, and see if the Pi boots normally. If it does, go ahead and make the changes to the file)

With the modified device tree file loaded at boot time, the PWR_Gate input is set high at power up, and it stays there until the UPS program has taken control.
Problem solved.

PS You need to be aware that the original dts file can change without notice with newer kernels, or when new Pi models are added. Using an overlay seems to be safer, but at this moment, I am not aware of a way to force a pin as output high or low, without a driver. This needs further investigation. (I have a post in the device tree section about this)

Enjoy!
Last edited by paulv on Sun Jan 03, 2016 8:12 am, edited 2 times in total.

Return to “Automation, sensing and robotics”