Laptop Battery Savior!
I've always been bad at caring for my electronics. My old laptop's battery is so damaged, it lasts all of ten minutes. So, I want to do right by my new macbook and make sure I don't kill it's battery too!
I use my laptop mainly in the office so generally it would be connected to the charger all of the time. WIRED seems to think that this will kill my battery and that I should instead cycle between 80% and 40% capacity. Very well, I can't change my habits but I sure can make stuff to have the same effect.
I present to you, a thingy that switches off your laptop charger when your laptop reaches 80% charge and switches on when it reaches 40% charge, thus maintaining this mythical battery prolonging cycle.
Long live the laptop battery!
P.S. If you can't tell, I'm not completely sold on this 80%-40% business. If you have any comments on the same, please do share in the comments. Seemed like a cool thing to make anyway :P
How It Workz?
The idea is pretty simple, control the power socket which your laptop charger is connected to. When your battery reaches 80% charge, switch off the charger and when it reaches 40% charge, switch it back on. Since the laptop charger is completely disconnected from AC power, I reckon we'll end up saving some electricity as well. All this while we can leave the power cord connected to the laptop without worrying about the battery at all.
On the hardware end, we will use an arduino connected to a relay and a bluetooth module. The arduino is powered by a 5V wall wart plugged into mains and the laptop charger is plugged into the output of the relay. It will receive serial commands from the laptop to switch the relay on or off.
On the software end, we are going to use bash. It is the default language that runs on terminal in most major linux distros and also on OSx. Sorry, windows users! I have never used bash before so this will also be a sort of kind of tutorial on bash as I try to explain every command. I apologize in advance for any blunders or convoluted code!
Tools and Material...
Material:
* 5V charger (It shall get destroyed.. ish)
* AC port and plug (I got mine from OSH park, link, link2)
* 3mm Acrylic (I like smoke grey)
* Arduino Pro mini (chosen for the size, any of the others would do just as well)
* HC05 bluetooth
* Neopixel
* 3V or 5V relay (make sure the current draw is under 50mA otherwise add a switching transistor)
* 47 and 220 Ohm resistors
* Wire
Tools:
* Laptop with OSx or Linux
* Laptop charger
* FTDI adaper
* Laser cutter
* Acrylic cement (or acetone)
* Super Glue
* Optional: Clamps
* Wire nippers and strippers
* Soldering kit
* Phillips head screwdrivers
Assembly: Part 0
Before you can create, you must destroy. Here I'm destroying this USB charger to get the guts, this will power the arduino and also provide some more USB charging ports. As a general rule, you can never have enough USB ports.
Assembly: Part 1
Laser cut the case. File attached in Corel, AI and SVG formats.
Assembly: Part 2
Screw in the AC port and plug into acrylic as shown. Take care to take it slow, or the acrylic might crack(mine did).
Connect small wires to the Line and Neutral ends of both the plug and the port, and a long common earth wire.
Assembly: Part 3
Weld the square acrylic pieces together, making sure that the notches on top are aligned. Put some superglue on the inside of the circle and slide them onto the AC port and plug. The notch corresponds to the screw directly opposite the long earth pin. Make sure that both the plug and port stand horizontally on each side.
Assembly: Part 4
I'm sorry for not having better pictures of this process. Attach the plug and port on opposite sides of one of the large blank rectangular acrylic pieces. The orientation does not matter, so long as both of the pieces are in the same orientation. Put something heavy on top of the pieces as they weld, to make sure they are perfectly vertical.
Assembly: Part 5 (The Circuit)
The circuit seems pretty elementary to me, so I shall not go into much detail. If you have any questions, feel free to contact me or leave a comment, I'll be sure to respond.
Glue the HC05 to the back of the Arduino pro mini, making sure to leave the antenna exposed.
[Edit] Thanks @Hassanul_ for pointing out that back EMF from the relay might fry the arduino pin. This, in fact happened to me :P I've changed the circuit to reflect the same.
Assembly: Part 6
Solder the connections between HC05 and arduino. This is the only time where the wire length is dictated by the parts themselves rather than the enclosure we're putting them into. All of the other electronics are soldered together while putting the case together.
Assembly: Part 7
Solder wires to the relay, and the 5V adapter. Make sure to connect the live wire of the plug to the relay, and not the neutral wire. And make sure to use the normally closed(NC) pin of the relay, so that if something happens to the arduino or the 5V adapter, the system still charges the laptop.
Assembly: Part 8
Attach the acrylic piece with slots for the USB ports. Hot glue the electronics into place. Just take your time and don't get burnt!(I go burnt like twice)
Program the Arduino and Test
Be sure to test everything before closing the lid and sealing the box. Easiest way to do this is to open the serial terminal in arduino with the bluetooth port and hitting the a and b keys to check that the led changes color and your laptop charger switches on and off.
Now that we have a bluetooth controlled port, we are going to program our laptop to control this port to control the charge cycle of our battery.
Downloads
Turn on the Terminal!
NOTE: In the next few steps I'm gonna go into gory details of how things work on the laptop end of things. If you don't want to learn bash just skip to step 18 :)
Aren't UNIX based systems great! You can do everything from the terminal. Including checking on the battery status. Just type this in the terminal(don't actually type the dollar sign):
$ pmset -g batt
and you'll get an output like
Now drawing from 'AC Power'<br>-InternalBattery-0 64%; charging; 1:51 remaining present: true
if you're drawing from AC power or
Now drawing from 'Battery Power' -InternalBattery-0 64%; charging; 0:00 remaining present: true
if you're drawing from battery power.
All right, now we just need to parse the two important parameters, the charge percent and whether or not the charger is connected. grep to the rescue!
$ pmset -g batt | grep -c discharging
returns 0 if we are connected to the charger and 1 if we are drawing power from battery. Similarly, the battery percentage can be parsed using
$ pmset -g batt|grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'
which extracts any one, two or three digit numbers which are followed by a % sign. So in our case, it would just output
64%
the trailing % sign can be easily gotten rid of with a simple | sed 's/[^0-9]//g', so the following command outputs just the battery percentage
$ pmset -g batt|grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%' | sed 's/[^0-9]//g'
So far, we have only printed the desired output. To make decisions based on the outputs, we need to first put our output into variables. This how:
$ discharging=$(pmset -g batt | grep -c discharging)
puts the output into a variable called discharging. So, executing the above statement will no give any output. But don't take my word for it, you can see what is there in the discharging variable by typing
$ echo $discharging
Similarly,
$ percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g')
puts the battery percentage into a variable called percentage.
Armed with this knowledge, let us make decisions as to what we want to do.
Executing Bash Files
Before we go on to use the variable to decide what to do, we first need to be able to write code into a file and run it. Using your favorite text editor create a file called code.sh and type the following in there:
echo 'Hello world!'
Now, to execute the file, you should type ./code.sh in the terminal(be sure to change the working directory to the code's location first!). But, doing so now will only give an error
$ ./code.sh -bash: ./code.sh: Permission denied
To give the code execution privilege, use chmod:
$ chmod +x code.sh
executing with ./code.sh now will give the output Hello world! in the terminal.
$ ./code.sh<br>Hello world!
Congrats you can now write and execute bash programs! Back to the work at hand.
Sophie's Choice
We have two variables called discharging and percentage. Let us go on to implement the 80-40 rule.
First let us switch based on the whether or not the laptop is charging. Change the code.sh file to the follows:
percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g')<br>discharging=$(pmset -g batt | grep -c discharging)<br>if [ $discharging == 0 ] then echo charging else echo discharging fi
The first two lines should be familiar, they are simply going to create the variables percentage and discharging as before. Now, to check the battery percentage, a switch-case would do. Change the code to:
percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g') discharging=$(pmset -g batt | grep -c discharging) case $percentage in [0-9]|[1-3][0-9]) echo 'Battery < 40%' ;; [4-7][0-9]) echo 'Battery > 40% < 80%' ;; [8-9][0-9]|100) echo 'Battery > 80%' ;; esac
We are using case to check if the battery percentage is between 0-39%, 40-79% or 80-100%. Here's more info on using case.
Now, we want to to switch on charging if the laptop is discharging and battery percentage <40% and switch off charging when we are charging and battery percentage <40%. Let us combine the if and case statements to get the following code:
percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g')<br>discharging=$(pmset -g batt | grep -c discharging) if [ $discharging == 0 ] then echo charging case $percentage in [8-9][0-9]|100) echo 'Stop charging' ;; esac else echo discharging case $percentage in [0-9]|[1-3][0-9]) echo 'Start charging' ;; esac fi
That's it. This is the logic part of the code completed!
Talking Different Languages
Goodie, we now know what we want out arduino to do. Let's go tell it, shall we?
The arduino is connected via a serial over bluetooth chip HC-05. I absolutely love these because they are easy to setup and generally more stable than most wifi connections. The arduino sketch polls the software serial to which the HC05 is attached. It listens for two characters only, 'a' and 'b'. If it 'hears' an 'a' it switches on the relay and lights the neopixel green and if it 'hears' a 'b' it switches off the relay and lights the neopixel red. By default, the relay is switched on. The connection is made to the normally closed side of the relay so that if the 5V adapter stops working, or the arduino otherwise loses power, the laptop charger remains connected to power.
To communicate to the HC05, we must first pair the HC05 the laptop. I have used the following settings for the HC05:
NAME: AC_PORT<br>UART: 9600 baud, no parity, one stop bit<br>CMODE: 1 (Connect to any MAC ID)<br>ROLE: 0 (Slave)<br>PASSWORD: Hah, not telling you that!
Changing these settings is quite easy directly through AT commands or via this library. Once set-up, switch on your HC05 and pair it to your laptop. It should now appear as a new port in the Arduino>Tools>Port menu. Mine appeared as the name /dev/cu.AC_PORT-DevB (your's will most likely be different). To send data over bluetooth, you can you any serial terminal software like putty, zterm or even arduino serial monitor. But more importantly for our purposes, you can send data via the echo command thusly:
$ echo -ne 'a\r\n' > /dev/cu.AC_PORT-DevB
Note the > symbol which directs the output to the serial port, easy as pie! The -ne flag switches on the interpretation of characters followed by backslashes. The HC05 only releases data from serial buffer when it encounters the carriage return (\r) followed by newline (\n) characters. So here we are sending the character 'a' i.e. switch on the relay. Now all we have to do is add this to our previous code. The final code looks like this:
percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g')<br>discharging=$(pmset -g batt | grep -c discharging) if [ $discharging == 0 ] then echo 'charging' case $percentage in [8-9][0-9]|100) echo 'Stop charging' echo -ne 'b\r\nb\r\nb\r\n' > /dev/cu.AC_PORT-DevB <br>#################################### Modify above to match your port! ;; esac else echo 'discharging' case $percentage in [0-9]|[1-3][0-9]) echo 'Start charging' echo -ne 'a\r\na\r\na\r\n' > /dev/cu.AC_PORT-DevB #################################### Modify above to match your port! ;; esac fi<br><br>
I'm not sure why, but the connection seems finicky, maybe because the devices are not continuously connected but instead get connected only for this brief transaction. Or maybe because the 5V converter is generating radio noise. whatever the reason, having multiple instances of sending data seems to help with the issue. If someone knows why this is happening, I'd love to know!
Setting It on Repeat
We have now written a script to check the status of the battery and charging and communicate with the arduino to switch the charger on and off. But, if we have to manually execute the script, it kind of kills the point of automation, doesn't it? So, we are going to set up a cron job to execute this script every five minutes. I highly recommend this article for in depth info on cron.
To setup a cron job, type the following into your command line
$ env EDITOR=nano crontab -e
This will open up your crontab file. To execute our script every minute, add the following lines to the file:
#AC port switching
*/5 * * * * sh /path/to/script/code.sh > /dev/null
The first parameter, */5 makes the job run every five minutes. The '> /dev/null' bit makes sure that it doesn't generate update emails every time it runs the job. Save the file and there you go!
The Easy Way
Hi, if you've read through the setup you're good to go! If you're joining me directly from step 13, here's what you need to do:
Create a file, code.sh file somewhere in your home directory and put the following in there:
#!/bin/bash
# Aug 7 2016 # Creative Commons, you know the drill :) percentage=$(pmset -g batt| grep -o '[0-9]%\|[0-9][0-9]%\|[0-9][0-9][0-9]%'| sed 's/[^0-9]//g') discharging=$(pmset -g batt | grep -c discharging) if [ $discharging == 0 ] then echo 'charging' case $percentage in [8-9][0-9]|100) echo -ne 'Stop charging \a' echo -ne 'b\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\nb\r\n' > /dev/cu.AC_PORT-DevB #################################################################################################################################### Modify above to match your port name ;; esac else echo 'discharging' case $percentage in [1-3][0-9]|[0-9]) echo -ne 'Start charging \a' echo -ne 'a\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\n' > /dev/cu.AC_PORT-DevB #################################################################################################################################### Modify above to match your port name ;; esac fi
Use chmod to make it executable. If the port name of your bluetooth module is not /dev/cu.AC_PORT-DevB change the name to match the name you see in Arduino>Tools>Port menu in the two locations indicated in the code. Then setup a cron job to run that file every few minutes. Done.
All Done!
Great! You can now plug your contraption into an AC port, plug in your laptop into the other end and enjoy a long lived laptop battery.
This has been my first foray into bash, so I might have made some blunders. But thankfully that also means I have a sympathetic ear for your frustrations in trying to get this to work. If you have any questions, feel free to comment!
Thanks for reading and happy making!