Show by Label

Monday, June 13, 2016

_HowTo: Simple Method to Log Charging and Discharging of Li-Ion and Lipo cells

For my Raspberry Pi automatic power supply design with a UPS function (see another post), I wanted to learn more about the charging and discharging characteristics of Li-Ion and Lipo cells.

In several of my supply designs, I use the Adafruit Powerboost 1000c circuit. It uses an MCP73871 charge controller, that is set to deliver up to 1 Amp of charge current. It does not use a Thermistor to monitor the cell temperature, and I wanted to use a variation of cells.

The cells I tested were a TR 18650 with 2400mAh, a TR 14500 with 1200mAh, both are Li-Ion cells, and one small Lipo cell, model 043040 with 500mAh.

Before I modified the Powerboost circuit to limit the charge current to 500mA or a little less, I wanted to profile the charge/discharge characteristics of these three cells.

I used a RPi Model 3 powered by my automatic UPS supply, and I used another RPi, a Model (1) B, together with an A-2-D convertor circuit to monitor the current of the cells powering the Model 3.

The monitoring circuit is based on yet another post, and modified to measure the cell voltage together with the charging or discharging currents. Here is the circuit:

I used the following Python script to show the progress on the console and log the results into a file that I could upload to Excel to graph curves.


#!/usr/bin/python
#-------------------------------------------------------------------------------
# Name:        MCP3002 Measure 5V
# Purpose:     Measure the voltage and current of a Li-Ion cell
#
# Author:      paulv
#
# Created:     22-10-2015, Modified 10-06-2016
# Copyright:   (c) paulv 2015, 2016
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import spidev # import the SPI driver
from time import sleep, time, strftime
import subprocess
import sys
import os

# ==== constants
__author__ = 'Paul W. Versteeg'
VERSION = "1.0"

DEBUG = False
vref = 5.0 * 1000 # V-Ref in mV (Vref = VDD for the MCP3002)
resolution = 2**10 # for 10 bits of resolution
cal_CH0 = 0 # calibration in mV
cal_CH1 = 0
interval = 10       # interval between measurements in seconds
sample_interval = 2 # interval between samples in seconds
log_interval = interval - (2 * sample_interval) # log interval in seconds

# MCP3002 Control bits
#
#   7   6   5   4   3   2   1   0
#   X   1   S   O   M   X   X   X
#
# bit 6 = Start Bit
# S = SGL or \DIFF SGL = 1 = Single Channel, 0 = \DIFF is pseudo differential
# O = ODD or \SIGN
# in Single Ended Mode (SGL = 1)
#   ODD 0 = CH0 = + GND = - (read CH0)
#       1 = CH1 = + GND = - (read CH1)
# in Pseudo Diff Mode (SGL = 0)
#   ODD 0 = CH0 = IN+, CH1 = IN-
#       1 = CH0 = IN-, CH1 = IN+
#
# M = MSBF
# MSBF = 1 = LSB first format
#        0 = MSB first format
# ------------------------------------------------------------------------------


# SPI setup
spi_max_speed = 1000000 # 1 MHz (1.2MHz = max for 2V7 ref/supply)
# reason is that the ADC input cap needs time to get charged to the input level.
CE = 0 # CE0 | CE1, selection of the SPI device

spi = spidev.SpiDev()
spi.open(0,CE) # Open up the communication to the device
spi.max_speed_hz = spi_max_speed


def read_mcp3002(channel):
    '''
    Function to read the data channels from an MCP300 A-2-D converter

    Control & Data Registers:
    See datasheet for more information
    send 8 bit control :
       X, Strt, SGL|!DIFF, ODD|!SIGN, MSBF, X, X, X
       0, 1,    1=SGL,     0 = CH0  , 0   , 0, 0, 0 = 96d
       0, 1,    1=SGL,     1 = CH1  , 0   , 0, 0, 0 = 112d

    receive 10 bit data :
       receive data range: 000..3FF (10 bits)
       MSB first: (set control bit in cmd for LSB first)
       spidata[0] =  X,  X,  X,  X,  X,  0, B9, B8
       spidata[1] = B7, B6, B5, B4, B3, B2, B1, B0
       LSB: mask all but B9 & B8, shift to left and add to the MSB

    '''

    if channel == 0:
        cmd = 0b01100000
    else:
        cmd = 0b01110000

    if DEBUG : print"cmd = ", cmd

    spi_data = spi.xfer2([cmd,0]) # send hi_byte, low_byte; receive hi_byte, low_byte

    if DEBUG : print("Raw ADC (hi-byte, low_byte) = {}".format(spi_data))

    adc_data = ((spi_data[0] & 3) << 8) + spi_data[1]
    return adc_data



def write_log(msg):
    '''
    Function to create a log of the readings with a time stamp.

    The fiels are seperated by a tab, so the file can be easily imported into
    an Excel spreadsheet to graph the results.

    The log results are appended, so the log file should be deleted for every
    measurement.

    '''
    try:

        dstamp = strftime("%d-%m-%Y")
        tstamp = strftime("%H:%M:%S")

        # open the log file and append results
        with open("/home/pi/lipo.log", "a") as fout:
            # Tabs are used to seperate the fields so technically it's not a real CSV format.
            # MS-Excel reads it anyway.
            fout.write (str(dstamp)+"\t"+(tstamp)+"\t"+str(msg)+"\n")

    except Exception as e:
        return(1)



 


def main():
    try:
        # create and setup the log file
        if not os.path.isfile('/home/pi/lipo.log'):
            # create the file and set the access mode
            subprocess.call(['touch /home/pi/lipo.log'], shell=True, \
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            subprocess.call(['chmod goa+w /home/pi/lipo.log'], shell=True, \
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        write_log("\n===== Starting Log =====")

        print("Voltage and Current log from Li-Ion/Lipo cell")
        print("SPI max sampling speed = {}".format(spi_max_speed))
        print("V-Ref = {0} mV, Resolution = {1} bit".format(vref, resolution))
        print("SPI device = {0}".format(CE))
        print("-----------------------------\n")

        while True:
            # average three readings to get a more stable result
            Vdata_1 = read_mcp3002(0) # get CH0 input (Volt)
            Cdata_1 = read_mcp3002(1) # get CH1 input (Current)
            sleep(sample_interval)
            Vdata_2 = read_mcp3002(0) # get CH0 input
            Cdata_2 = read_mcp3002(1) # get CH1 input
            sleep(sample_interval)
            Vdata_3 = read_mcp3002(0) # get CH0 input
            Cdata_3 = read_mcp3002(1) # get CH1 input

            Vdata = (Vdata_1+Vdata_2+Vdata_3)/3
            if DEBUG : print("V_Data (bin)    {0:010b}".format(Vdata))
            v_result = round((((Vdata * vref) / resolution)+ cal_CH0),2)
            if DEBUG : print ("V_result {} mV").format(v_result)
            voltage = round(v_result/1000.0,2) # convert to mV with 2 decimals
            print("Voltage  : {} V".format(voltage))

            Cdata = (Cdata_1+Cdata_2+Cdata_3)/3
            if DEBUG : print("C_Data (bin)    {0:010b}".format(Cdata))
            Vcurrent = round((((Cdata * vref) / resolution)+ cal_CH1),2)
            if DEBUG : print ("Vcurrent : {} mV").format(Vcurrent)
            current = int(Vcurrent/5) # convert to mA (gain 50, R = 0.1 Ohm)
            print("Current  : {} mA".format(current))
            if DEBUG : print("-----------------")

            # log the results
            msg = str(voltage) + "\t V\t" + str(current) + "\tmA"
            write_log(msg)
            sleep(log_interval) # wait and loop back

    except KeyboardInterrupt: # Ctrl-C
        if DEBUG : print "Closing SPI channel"
        spi.close()


if __name__ == '__main__':
    main()



Here is the charge curve for a tiny 043040 Lipo cell :












This is the one I use: http://www.ebay.com/itm/251493181437

The "noise" that you see on the voltage and current graphs can be caused by the chemical process that takes place. I read somewhere that this is a "noisy" process.

This charge curve shows a charge current that is a little too high for comfort. The specification for the cell ( http://www.powerstream.com/p/H043040SP%20with%20BMS.pdf ) lists a maximum charge current of 1C, but a more safe value is 80% of that. The graph shows that there is a short period of time where the current is more than 630mA, so I needed to modify the Powerboost circuit to lower the maximum charge current.

This is quite simply done by changing one resistor value (R16, located just above the USB connection) from 1K0 to 2K2 (resulting in a maximum of 454mA), unfortunately it is an 0805 SMD component, so not that easy. Here is the schematic of the Powerboost :
https://cdn-learn.adafruit.com/assets/assets/000/024/638/original/adafruit_products_sch.png?1429650091

Here is the charge graph with the limited charge current now topping at 454mA.











 And here the discharge curve for the 043040 cell:










The charge provides about 20 minutes of power to an Rpi-3 (in rest, idling!) at an average of about 450mA consumption current. The Powerboost circuit warns for a low-bat level, and my hardware removes the power from the RPi when that happens. The trip voltage is somewhere below 3.5-3.25V (it varies somewhat depending on the fall-off curve).


Here is the charge graph for a 14500 Li-Ion cell with the unmodified Powerboost:











This is the discharge graph for the 14500 cell:












And finally the charge graph for a Li-Ion 18650 with 2400mAh with the unmodified Powerboost :












And the 18650 discharge graph:










Note that it took 4 hours to charge this 2400mAh cell after it powered the Rpi-3 (idling!) for 3 hrs (at an average of  an 450mA consumption rate). Also notice the almost perfect linear discharge level. I read somewhere that they use these cells for the batteries in hybrid and electric cars like the Tesla.

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Saturday, June 4, 2016

_HowTo: Raspberry Pi with auto restart

Based on my one button start-stop design, I modified the circuit so the RPi can be used in autonomous or embedded applications.

https://www.raspberrypi.org/forums/viewtopic.php?uid=52264&f=37&t=150358&start=0

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

_HowTo: Update on RPi Watchdogs

This post was concocted several years ago to show various ways to make sure applications or the kernel are protected from hang-up issues. Required when you run a server application, security camera or network related devices.


Here is a quick and concise summary of the various ways to use the watchdog functionality.
After all the trouble some of us went through to master the watchdog, it basically distilled down to three different methods.

These three methods cannot be combined because the /dev/watchdog device is claimed by either of the methods.

The watchdog device is already activated at boot time for all three methods.
I tried Method 1 and Method 2, which are RPi specific, on an RPi Model B3+ running Stretch, and on the RPi Model 4 running Buster. Both methods work fine on either RPi. Method 3 is very generic, and only needs one adjustment to avoid a bug.

Method 1

The easy shell method is as follows:
With a little script, you can add protection for kernel and user-space program hang-ups.
You start that process by sending a period "." to /dev/watchdog. This will kick-off what I would call a keep-alive session. You, or your program now needs to continue to send a "." to the /dev/watchdog within a 15 second period. If you don't, the RPi will reboot automatically. You can send the character "V" to the device to cancel this process.

You can use the following command to test this out - watch out however, the RPi will reboot in 15 seconds if this is all you do! :

sudo sh -c "echo '.' >> /dev/watchdog"

Every time you resend this command within a 15 second window, the watchdog counter will be reset. If you stop doing this or wait for more than 15 seconds, the timer overflows, en the RPi gets rebooted.

Creating and activating the following little script (from user sparky777), will protect the RPi for kernel hang-ups.

#!/bin/bash
echo " Starting user level protection"
while :
   do
      sudo sh -c "echo '.' >> /dev/watchdog"
      sleep 14
   done

When this script gets installed by init.d or systemd at boot time, it most likely runs as root so there is no need to do the "sudo sh -c" trick, you can simply use "echo . >> /dev/watchdog" instead.
I took the easy way and installed it with cron. Just add
@reboot /home/pi/name-of-program
and reboot to install.

When this script runs, there is now protection for kernel related issues. This can be tested with the so called fork bomb.
Make sure the script runs.
Simply type the following sequence at a prompt and then hit return to launch the fork-bomb.

: (  ){ : | : &  }; : 

The RPi will reboot in about 15 seconds.


Method 2
The second method with the same functionality can be obtained by using systemd.

To let systemd use the watchdog, and to turn it on, you need to edit the systemd configuration file.


sudo nano /etc/systemd/system.conf 
and change the following line:
#RuntimeWatchdogSec=
to:
RuntimeWatchdogSec=10s
Fifteen seconds is the maximum the BCM hardware allows.
I also suggest you activate the shutdown period protection by removing the '#' in front of the next line.
ShutdownWatchdogSec=10min

After a reboot, this will activate and reserve the watchdog device for systemd use. You can check the activation with :

dmesg | grep watchdog

It should report something like this on an RPi M3+ with Stretch:
[ 0.784298] bcm2835-wdt 3f100000.watchdog: Broadcom BCM2835 watchdog timer
[ 1.696537] systemd[1]: Hardware watchdog 'Broadcom BCM2835 Watchdog timer', version 0
[ 1.696628] systemd[1]: Set hardware watchdog to 10s.
The kernel will now update the hardware watchdog automatically every 10/2 seconds. If there is no kernel activity for 10 seconds, the RPi reboots.
This means that there is a default protection for kernel related issues. This can be tested with the so called fork bomb, see above.

If you want the user-space application protection capability, you have to use the systemd API within your program to do that. This is covered in a later post.

Method 3


The third method is not RPi specific and uses a rather large and sophisticated daemon package (pretty much legacy now) that allows you to set many different parameters that will be able to reboot the RPi. After installation you can use

man watchdog

For more information, or go here: https://linux.die.net/man/8/watchdog

The package needs to be installed first.

sudo apt-get install watchdog

Because this is a wide spread legacy package, I'm not going to cover that in detail here.
To set some of the parameters the watchdog daemon should watch :

nano /etc/watchdog.conf

For the fork bomb test I took away the "#" marks from the following lines:
# This is an optional test by pinging my router
ping=192.168.1.1
max-load-1 = 24
min-memory = 1
watchdog-device = /dev/watchdog
watchdog-timeout = 15
Note: The last line is very important and Rpi specific. If this command is not added, you get a bit of a cryptic error (run sudo systemctl status watchdog.service) :
cannot set timeout 60 (errno = 22 = 'Invalid argument')
This is caused by the default wdt counters used in other Linux systems, mostly handlingt 60 seconds. But because the RPi wdt counter on the SOC only handles a maximum of 15 seconds, this line must be added, otherwise the package won't work at all.
Unfortunately, this is a bug that the Foundation missed and the default 15 seconds should have been programmed into the kernel, or added by default in the watchdog.conf file.


---------------------------------------------------------------------------------------------------------------------------------
Using the systemd API to let a program control the watchdog.

Below I will show how to add extra support for your own (Python) application by using the systemd API and framework.

If you want to use the systemd method of using a software watchdog to add control to your own application program, you can use the following method to implement that.

As I showed above, you use the hardware BMC watchdog system to reboot the RPi when the kernel gets unresponsive, or when systemd is no longer operational.

A higher level of control can be added by a software watchdog. Systemd provides that, plus an interface (API) to implement that.
The combination of the two provide the Supervisor chain (in systemd speak).

There are two steps to implement this method.

1. You need to provide a service configuration file for systemd to instruct it what to do.
2. You need to add a few things to your own application to make it all work in this environment.

In essence, you are going to ask systemd to initiate the watchdog, and your application needs to "ping" it at regular intervals. If the application fails to do that, systemd will take action and can ultimately reboot the RPi.

I wrote a systemd service file that will let you test a number of elements.

# This service installs a python test program that allows us to test the
# systemd software watchdog. This watchdog can be used to protect from hangups.
# On top of that, when the service crashes, it is automatically restarted.
# If it crashes too many times, it will be forced to fail, or you can let systemd reboot
#

[Unit]
Description=Installing Python test script for a systemd s/w watchdog
Requires=basic.target
After=multi-user.target

[Service]
Type=notify
WatchdogSec=10s
ExecStart=/usr/bin/python /home/pi/systemd-test.py
Restart=always

# The number of times the service is restarted within a time period can be set
# If that condition is met, the RPi can be rebooted
#
StartLimitBurst=4
StartLimitInterval=180s
# actions can be none|reboot|reboot-force|reboot-immidiate
StartLimitAction=none

# The following are defined the /etc/systemd/system.conf file and are
# global for all services
#
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#
# They can also be set on a per process here:
# if they are not defined here, they fall back to the system.conf values
TimeoutStartSec=2s
TimeoutStopSec=2s

[Install]
WantedBy=multi-user.target

Details can be found if you look for systemd.service(5)


I also wrote a Python script that lets you play with this system and experiment to you hearts delight.

#!/usr/bin/python2.7
#-------------------------------------------------------------------------------
# Name:        systemd daemon & watchdog test file
# Purpose:
#
# Author:      paulv
#
# Created:     07-05-2016
# Copyright:   (c) paulv 2016
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import sys
import os
from time import sleep
import signal
import subprocess
import socket

init = True

def sd_notify(unset_environment, s_cmd):

    """
    Notify service manager about start-up completion and to kick the watchdog.

    https://github.com/kirelagin/pysystemd-daemon/blob/master/sddaemon/__init__.py

    This is a reimplementation of systemd's reference sd_notify().
    sd_notify() should be used to notify the systemd manager about the
    completion of the initialization of the application program.
    It is also used to send watchdog ping information.

    """
    global init

    sock = None

    try:
        if not s_cmd:
            sys.stderr.write("error : missing s_cmd\n")
            return(1)

        s_adr = os.environ.get('NOTIFY_SOCKET', None)
        if init : # report this only one time
            sys.stderr.write("Notify socket = " + str(s_adr) + "\n")
            # this will normally return : /run/systemd/notify
            init = False

        if not s_adr:
            sys.stderr.write("error : missing socket\n")
            return(1)

        sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
        sock.sendto(s_cmd, s_adr)
        # sendto() returns number of bytes send
        # in the original code, the return was tested against > 0 ???
        if sock.sendto(s_cmd, s_adr) == 0:
            sys.stderr.write("error : incorrect sock.sendto  return value\n")
            return(1)
    except e:
        pass
    finally:
        # terminate the socket connection
        if sock:
            sock.close()
        if unset_environment:
            if 'NOTIFY_SOCKET' in os.environ:
                del os.environ['NOTIFY_SOCKET']
    return(0) # OK


def sig_handler (signum=None, frame = None):
    """
    This function will catch the most important system signals, but NOT a shutdown!
    During testing, you can use this code to see what termination methods are used or filter
    some out.

    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 or the shutdown procedure
    """

    try:
        print "\nSignal handler called with signal : {0}".format(signum)
        if signum == 1 :
            sys.stderr.write("Sighandler: ignoring SIGHUB signal : " + str(signum) + "\n")
            return # ignore SSH logout termination
        sys.stderr.write("terminating : python test script\n")
        sys.exit(1)

    except Exception as e: # IOerror 005 when terminating the SSH connection
        sys.stderr.write("Unexpected Exception in sig_handler() : "+ str(e) + "\n")
        subprocess.call(['logger "Unexpected Exception in sig_handler()"'], shell=True)
        return

def main():

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

    # get the timeout period from the systemd-test.service file
    wd_usec = os.environ.get('WATCHDOG_USEC', None)
    if wd_usec == None or wd_usec == 0:
        sys.stderr.write("terminating : incorrect watchdog interval sequence\n")
        exit(1)

    wd_usec = int(wd_usec)
    # use half the time-out value in seconds for the kick-the-dog routine to
    # account for Linux housekeeping chores
    wd_kick = wd_usec / 1000000 / 2
    sys.stderr.write("watchdog kick interval = " + str(wd_kick) + "\n")

    try:
        sys.stderr.write("starting : python daemon watchdog and fail test script started\n")
        # notify systemd that we've started
        retval = sd_notify(0, "READY=1")
        if retval <> 0:
            sys.stderr.write("terminating : fatal sd_notify() error for script start\n")
            exit(1)

        # after the init, ping the watchdog and check for errors
        retval = sd_notify(0, "WATCHDOG=1")
        if retval <> 0:
            sys.stderr.write("terminating : fatal sd_notify() error for watchdog ping\n")
            exit(1)

        ctr = 0 # setup a counter to initiate a watchdog fail
        while True :
            if ctr > 5 :
                sys.stderr.write("forcing watchdog fail, restarting service\n")
                sleep(20)

            sleep(wd_kick)
            sys.stderr.write("kicking the watchdog : ctr = " + str(ctr) + "\n")
            sd_notify(0, "WATCHDOG=1")
            ctr += 1


    except KeyboardInterrupt:
        print "\nTerminating by Ctrl-C"
        exit(0)


if __name__ == '__main__':
    main()

The comments should give you an idea of what is needed. In a nutshell, the application needs to signal systemd that it has finished the initialization. At regular intervals, the software watchdog is updated. There is a fail condition in the code above that will mimic a hung application.

Here is how you install and test this all.
Open an editor:

nano systemd-test.service

Copy and paste the service code above into the editor. Save the file and close the editor. Copy this file into the systemd structure with :

sudo cp systemd-test.service /etc/systemd/system

Open an editor again:

nano systemd-test.py

Copy and paste the Python code above into the editor. Save the file and close the editor. Make the python script executable :

chmod +x systemd-test.py

Run the service script in the systemd environment :

sudo systemctl start systemd-test

Watch what is going on with

tail -f /var/log/syslog

After 4 failures and automatic restarts of the python script, systemd declares it a failed state. You can also let the RPi reboot when this happens and all you need to do is to change StartLimitAction=none to StartLimitAction=reboot in the systemd-test.service file.

If you would like to test the application within the boot process, run this :

sudo systemctl enable systemd-test

After a reboot, you can again watch it all by using the above tail command again.
If you decide to change the Python script, you can do that while the system is running. At the next restart, the new code is automatically loaded and executed. If you want to change parameters in the .service file, you can do that too, but you need to activate and reload those changes. You do that with

sudo systemctl daemon-reload

and then

sudo systemctl restart systemd-test

I had great fun to discover all the possibilities systemd now offers me to add better control to my own scripts.

Please chime in if you have improvements or suggestions!

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Wednesday, April 27, 2016

_HowTo: RPi Power Supply with 1 button start-stop and li-ion UPS


In my search to get to an improved power supply for the RPi, I have yet designed another version that is more simple to build and use compared to previous designs. On top of that, it's much smaller and does not need another housing.

This one is based on the Adafruit PowerBoost 1000c Li-ion charger and boost device. It combines very well with my one-button-start-stop design.

Have a look here:

www.raspberrypi.org

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Sunday, March 20, 2016

_HowTo: One Button to Halt and Restart the Pi

I published a post on the Raspberry Pi forum that shows how to do this with only a button, two resistors and 1 capacitor.

https://www.raspberrypi.org/forums/viewtopic.php?uid=52264&f=37&t=140994&start=0

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Thursday, December 17, 2015

_HowTo: Rotary Encoders & Raspberry Pi

After having found a simple and reliable solution for a rotary encoder using a PicAXE (see demistifying rotary encoders), I figured that I could easily port that solution to my Pi's.

Well, no! The Pi is so much faster that the solution did not port or translate, see this post for Details on how I developed one for the Pi. https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=126753&p=848012#p848012

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

_HowTo: Demistifying Rotary Encoders

For a new project, I needed a way to reduce parts and complexity, so it was time to finally bite the bullit and start working on a microcontroller. My experience with embedded controllers dates back at least 35 years, which is why I had been putting the decision off for a long time. Things changed in that period, and I was not keen to dive in yet. After investigating the available solutions, I decided on the PicAXE family due to the very complete design environment, and the availability of a programming language other than C or C++.

The new project needed a large selection method for frequencies and voltages, and traditional rotary switches became expensive and complex. So I decided to use a rotary encoder together with an embedded controller. It also solved the problem of a complicated frontpanel, because I now could use a display driven by the controller.

While researching rotary encoders, I learned a lot about decoding them, and eventually decided on a method that is adequate for my application.

I wrote two posts on the PicAXE forum to explain this in more details, and here is the link: http://www.picaxeforum.co.uk/showthread.php?28222-Demystifying-Rotary-Encoders-(one-more-time)-Part-1-2

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Monday, December 14, 2015

_HowTo: Using a single push-button to start/stop/powerdown the Raspberry Pi

A while back I did some work by another forum member to incorporate an interesting chip with Raspberry Pi's. It really lacks a "PC" like start/stop button, but this was left out most likely for cost reasons. There have been many designs made to solve this challenge.

Linear came up with a couple of chips that helps to solve this problem, and with the help of the Raspberry Pi foundation, an overlay was created to get a GPIO port that can signal the end of the Halt status.

Based on that work, I created a design that is well documented and rather easy to build. While I was at it, I came up with a couple more designs that uses this chip, the LTC2951-1, although there are several in this family. Unfortunately, these chips are hard to get, not in-expensive at about $5 each, and come in a tiny, very tiny SMD package. On top of that, MOSFET's are used to switch the power, and the right ones (with a low RDS-on) are also only available in SMD packages.

Eventually, I was able to come-up with yet another design that is even more simple, and only uses 4 resistors and 1 capacitor, in addition to a push-button.

Here is the link to the posts on the Raspberry Pi forum:
https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=128019

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

Monday, October 26, 2015

_HowTo: Adding an Analog Output to the Pi (DAC)

Here is a post I wrote on the Raspberry Pi forum about adding an analog output to the Pi by using a DAC.

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

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw


_HowTo: Adding an Analog Output to the Pi (PWM)

Here is a post that I put on the Raspberry Pi forum about using the Pulse Width Modulation feature to create a (static) output voltage. The result is more accurate than most would expect.

Here is that post : https://www.raspberrypi.org/forums/viewtopic.php?f=37&t=124130

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw

_HowTo: Adding an Analog Input to the Pi (ADC)

I wrote a post on the Raspberry Pi forums about adding an analog input to the Pi by using an ADC. The application I used to describe it was to measure the 5V supply to the Pi, which is still a major source of problems and confusion.

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

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw


Tuesday, August 11, 2015

_HowTo: Controlling a temperature driven Fan with PWM

Because I need a fan for my power supply project, I started a little project to do that.
I don't like noise or hum, so I wanted to use a fan that would be off when not needed.

Typically, by using Pulse Width Modulation (PWM), rather than a voltage, you can precisely control the speed of the fan, and ramp it up when needed.

I was able to find some clever methods to implement this, and pieced together this design:

The building blocks are relatively simple. I use a DC-DC convertor, not a linear one, to bring the 24V DC to 5V, because a linear LM7805 would get too warm burning off the excess voltage.
I use a comparator with hysteresis to determine the starting point of the fan, based on the temperature reading (in ohms) of a thermistor that is mounted on the heat sink. I don't have the datasheet for the thermistor, but found that it gets from 10K at room temperature to about 5K when the heat sink is getting really hot.

I need 5V for the fan, because I have one that is only 10mm thick, and that is what I have room for.

The clever trick of this circuit lies in the fact that the Control Voltage (CV) input from a 555 timer is used to control the PWM.

The 555 is producing pulses, and the pulse width, and also a bit of the frequency is varied by applying a voltage to the CV input. The output of the 555 goes to a FET that drives the fan.

The whole thing works very well, although the low pulse frequency can be heard from the fan, so I needed to use C4 and C5 to remove that chirping sound.

There are two disadvantages of this design.
One is that you cannot regulate the full 100% of the pulse width. The minimum is OK at about 30%, it let's the fan spin very slowly, but the maximum is only about 70%.
The other disadvantage is that you cannot increase the frequency of the pulses above the hearing frequency of 20KHz, because then the effect of the thermistor on the PWM range is greatly reduced. 

In my application, that is not good enough, I need to be able to get to the maximum fan speed in order to keep things cool.

There are special fan/motor controllers that allow you to do that, so I have a couple of TC648VPA chips on order. Stay tuned.

The chips arrived and I made two different circuits. One for a 12V DC Fan I also ordered, and one for the 5V DC fan.  I used an Excel spreadsheet that is available on the MicroChip website to calculate the resistors (R1 and R2) to get the best starting and maximum fan speeds.




The TC648 works really well, and is a nice addition to my toolbox. Next step is to put the circuits on vero board and install them in the power supplies.

After playing and experimenting, I decided to make a few changes to the circuit. First, I implemented the VAS  pin. The explanation says to set the Auto-shutdown threshold with this pin, but it also sets the turn-on threshold. The nice thing is that the fan will be driven to full speed for a short period, making sure it turns on right away. This was a problem I noticed with the earlier circuit.

Second, I deleted the resistor that was in parallel to the NTC. I felt that with a 10K NTC, it didn't do much. I also experimented with C7, which sets the frequency to see if I could remove the audible noises at lower speeds. That only worked with a 10nF cap, but then the PWM spread is limited and therefore also the speed control. So I resorted to using the 1uF value.

BTW, after a lengthy search, I found the information about the NTC I was using, because I wanted to know the temperature curves. Unfortunately, the shop I got them from didn't mention the type or manufacturer. I have the TDK B57045K0103K000.

I also found a much more elaborate data sheet for the device here: http://ww1.microchip.com/downloads/en/DeviceDoc/21755c.pdf

And here is the latest version of the schematic:


If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw


_HowTo: RPi Start-stop button with shutdown and power-off features

I recently finished a design, based on an idea from sparkyPi, a raspberry Pi forum member, where he was using a rather special purpose chip to add start-stop functionality. He needed a special Device Tree feature to create a signal that the Pi sends out when the shutdown sequence has finished, and even got help from the designers to complete that. His attempts led me to find yet another method to do the same, but without some side effects that the other method introduces.

In the meantime, I ordered some of the chips (LTC2951-1 : Start-Stop Push Button Controller) he used, and I was able to built a working circuit that does not even need Device Tree features. Here is the link to the post: https://www.raspberrypi.org/forums/viewtopic.php?f=41&t=117863

In this post you'll also find the links to the other ones.

Enjoy!

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw


Tuesday, August 4, 2015

Building a Dynamic DC Power Load (updated)



I finally got around to building my own DC Power Load.

My noise problems with the DC Power Supply, as described in another post, forced me to build this tool so I could measure the results.

There are many designs on the web if you care to take a look, but very few had a dynamic load feature, which is what I wanted.

I finally settled on one particular design and modified that through some trial and errors. Here is my starting point that I got from the work of Peter Oakes:

Here is my version:


Let’s go over the basic building blocks.

In order to have a stable setting of the load, independent of supply voltage changes, I decided to use a voltage reference chip. The voltage output of the chip was selected based on the maximum current I wanted to sink which is 5 Amps. By using a shunt of 0.1Ohm, 5 Amps translates into 500mV.
So I selected an LM 358 with 1.25V output. By using a resistor divider together with the current setting pot meter, I created a 500mV level, so the pot meter could supply 0..500mV, or 0..5Amps.
By just changing just one resistor, I can later change the load current to a maximum level of 12.5A.

The voltage over the shunt is fed to the negative input of U1, which will try to keep this input equal to the positive input that gets the desired reference voltage, by varying the output. The output of the Op Amp is connected to a MOSFET that can handle plenty of Volts, Amps and Watts.

To set the current sink, or the load, I used the output of the current setting pot meter and used a toggle switch to feed that to the Voltmeter of my display. The voltage from the pot meter in milli Volts corresponds directly to the current in Amps. (I first used a 10x op amp to convert the current to volts 1:1, but that’s overkill of you can multiply by 10 yourself)

As I learned with my power supply project, these Volt/Amp displays inject a lot of noise into the supply, so I used a series resistor, Zener and two capacitors to stop that from entering the rest of the circuit.

If S3 is in the Direct setting, the reference voltage of the pot meter is fed into the op amp. In the Dynamic setting I use a separate input coming from a function generator or equivalent, in order to pulse the load at certain frequencies. This allows you to measure the rise and fall times or other responses of the supply under test.

To eliminate possible oscillations and to add stability, C7, C3, C2 were added. I also created a possibility to add a capacitor parallel to R4, to better drive the MOSFET, but did not need it at the moment. A Sobel filter circuit was added at the output terminals for the same reason.
Unfortunately, the Volt/Amp display unit I have does not allow floating measurements, which is why I had to put the shunt into the ground loop. This will add a rather small (25mOhm shunt) current error.

I have another V/A display on order  that can be modified to make the current measurement floating, which also allows high-side current shunts, which are better anyway. Again Peter Oakes is the one that figured this out. https://www.youtube.com/watch?v=A6VQhDioz_Q
In any case that current shunt of the modified meter can be placed between the Drain of Q1 and the 0.1 Ohm shunt.

The Gate of the MOSFET is “clamped” to ground by a small resistor, just to make sure a problem does not create an unexpected short to the output. This resistor should be soldered right on the pins of the MOSFET and the shunt, to take care of bad or broken wiring.
By using the dynamic load, I was able to measure a rise and fall time of less than 50uSec, more than adequate for my applications.

One word of caution. 
The MOSFET is capable of 55V, 110A and 200W. With 30V and a couple of Amps, it does not even get warm on my rather small heat sink. The on resistance is only 8 milli Ohm, although we will never get that low because we don’t saturate the device. However, dealing with higher voltages and or higher currents increases the number of Watts rapidly. Remember that the two multiply!


Update : Aug 29, 2015

Over the last couple of weeks, several things have changed after the initial build. I received my new Volt/Amp meter, and modified that per the instructions by Peter Oakes. It now has a floating differential current sense circuit so I have put it in between the MOSFET's and the current sense shunt. I'm also very impressed with the accuracy of both meters. No more adjusting, and the decimal point and number of digits change like a "real" DMM, what it really is. On top of that, it has exactly the same accuracy as my DMM. Highly recommended!


I also installed a temperature controlled fan, it uses a nifty fan controller chip to do all that. Look at a separate post for details.

To get a better handle on the temperature at higher loads, I decided to use two MOSFET's in parallel. I was a little careless testing all this, thinking that this plus the fan would solve my heat problems. I was eager to start my tests of the two PSU's, which is why I built this tool in the first place. Well, I lost a MOSFET due to runaway thermal issues. Bummer!

Despite my search for information on how to put two power MOSFET's in parallel, all I could find (at the time), was the use of seperate gate resistors, to balance the two MOSFET's. Well, that didn't work very well. Due to the differences in the VGS for both devices, one took all the heat.

After swapping out the broken one, I was a little bit more careful, and noticed right away that there was a large difference in the thermal balance between the two. One was getting slightly warm, but the other too hot to touch. Apparently, the only real solution to the RDS(on) and therefore thermal balance issue is to build two circuits with separate op amps and sense resistors. I did not want to do that right away, and tried to see if I could get close enough. First I increased the value of the gate resistors, and also matched them to within 1 Ohm. That did not do anything to get a better balance. I then started to increase the value of the one one that got hot, but that did not produce the result I was after. In the end I used a 10K trimpot to set the VGS to a reduced level, and used my temperature probe to balance the two. I got to within a few degrees C after running the unit with a 1A load, such that the temperature was about 50 degrees C. It's not ideal, because I don't think the VGS delta has a linear relationship, but for now I'm happy.

Here is revision 3:

This design will most likely be further tuned while I'm going through the paces of testing and specifying my two power supplies, so stay tuned.

Update : August 30, 2015
Well, it didn't take too long for another refinement.

I kept mulling about the thermal balance challenge, and was not too happy about the comment I made earlier in the post above. While surfing for ideas for the parallel use of the power MOSFET's, I came across a comment that I took for face value, assuming the guy knew what he was talking about. I simply repeated his comment in the previous post that the "real" solution is to make two separate circuits. So to duplicate the op amp, FET and current sense resistor.

I kept thinking about that statement, and decided that it is misleading or even incorrect. Yes, you will get two circuits that together will share the load, but it does absolutely nothing to get a better load balance, or a better thermal balance. So, back to the drawing board.

Initially, I wanted to use two thermistors (or NTC's), thermally connected to each FET, and then use a comparator to decide if one was getting hotter than the other. The challenge was that I could not find an easy way to mount them on top of the MOSTFET's. Sure, I could have mounted them on the heatsink, but I would have lost some temperature sensitivity, with the heatsink acting as a dampener.

I then switched to using the junction temperature of transistors, but again could not find a good thermal connection to the FET's. I then went through my power transistor stack for ideas and found two excellent specimens. I happen to have quite some (very old, as in late 1970's) D40C5 darlington power transistors, that have a very flimsy heatsink. This turned out to be a perfect way to mount them with the same screw that also mounts the MOSFET, provided you also isolate the transistor tab, because it is the collector. I happen to have all the bits and bobs required, so I could continue.


BTW, the fact that I use darlington transistors, is only because I had them. You can use any TO22X kind of package, as long is you can thermally connect them to the MOSFET's.

In any case, whipping-up a small circuit to measure the delta temperature difference and drive a FET to drop the VGS on the gate of the FET that get's too hot was quite simple. By selecting a feedback resistor to create a small threshold, I managed to keep the temperatures within about 1 degree C. So whenever the temperature of Q2 id one degree C higher than that of Q1, the gate of Q2 is clamped and as soon as the temperatures are equal again, the gate is released again. Perfect!

Here is Revision 4:

Resistor R21 in the drain of the BS170 MOSFET is there to reduce the gate voltage, not to yank it to ground. With the resistor combination of R9, R14 and R21, the gate voltage drops from about 300mV,  just reducing the load and therefore temperature of Q2, while not affecting Q1, and therefore the output.

Just in case you are interested to use this idea in other applications, you need to be aware of two constraints. With this particular application, I'm not concerned about the self-heating of T1 and T2. I'm interested in the delta temperature difference, not the absolute difference. Also, the fact that the load will move from both MOSFET's to mostly one for shorter periods is of little concern for me. Both MOSFET's are individually capable of handling the full load.

Enjoy!

After having used the DC load for about a year, I was a little annoyed about the fact that I could not obtain a zero load at the output, despite the trimmer on IC1 and using a small negative voltage for it. Although the potmeter (R6) was getting down to zero Volt, the output of IC1 remained at 3V3, resulting in a 0.0059 Volt over the shunt (R12).

I finally figured out a way to adjust the potmeter range at the lowest position to have an output of zero load. The trick was to apply a very small negative voltage at the lower part of R6, instead of connecting that to ground. I used a tap on the two diodes that created a small negative voltage for IC1, in an earlier attempt to obtain a zero Volt output, and fed that -0.7V to an added 10K trimmer. With R6 in the lowest position, the new trimmer is now adjusted such that the output is showing no load.

Here is the new schematic with that change:

June 13 2017
While going through the design of a new power supply (a new post will be coming), I was not satisfied with the dynamic switching of the load. The pulse that is coming in from the BNC input actually switches the load hard on and off, so the maximum load is used, and there is no way to limit that.

I also didn't really need a fully variable input to the load, a simple pulse would suffice. During the power supply design, I actually put a 555 based timer on a breadboard and fed that to the DC load external input. I also added a few parts to make the output variable. I liked the results, so I put everything on a little prototype board and added it to the internals of the DC load.

Here is the schematic of the Pulse Generator addition:


Those that follow my posts will notice that I no longer use Eagle. A few months ago, I switched to Diptrace. I finally ended up frustrated by the difficulty in Eagle of adding custom parts, so I started to look around. I dismissed all the free ones, including K-Cad. That is just too cumbersome, and at this moment only for experts I think. An electronic pen-pal advised me to try Diptrace, and I have been using that ever since. My new power supply, discussed in another post has been designed that way, and I also made my very first pcb layout with it.

Anyway, I made Diptrace resemble the Eagle schematics a bit, because those still look visually pleasing.

OK, back to the Pulse Generator. I used a 555 based circuit that produces a pulse train with approx. 180mSec on, and approx. 70 mSec off periods. By just changing C3, you can change the frequency. At first, I used the 12V supply to power this circuit, but later changed my mind about that. I didn't want pulses with a 12Vp-p output roaring through my box, when I do sensitive measurements.

I changed the single throw switch that selected between static and dynamic inputs, to a double throw switch. The other side now switches the pulses off, by shorting the charging cap C3 to ground, such that the 555 pulses are off when the static mode is selected. It keeps things more quiet in the box. At first I switched the power to the 555, but turning it on produced an unwanted output glitch causing a high load on the output until the 555 settles.

The DC load will be switched on during the on-pulse, and fully off during the off-pulse. Because of the 5V supply, the output of the 555 timer is about 5V p-p. That signal is used for a trigger out signal. That signal now goes to the same BNC that was previously used as a pulse input. This signal typically goes to a scope to trigger it. This trigger output is an important feature because if you test a power supply, as an example, you want to see the effects of the load regulation while you switch dynamic loads on the power supply output. If the load regulation is any good, it will be very hard to see any effects on the output of the supply, so knowing where to look, based on the trigger signal, really helps a lot.

A second divider (R5 and the potmeter R6) attenuate the signal, so at the wiper of the potmeter, I will have 500mVp-p at maximum, and 0Vp-p at the minimum.  A voltage level of 500mVp-p will turn the DC load fully on, which is the same as the static load adjustment potmeter does. I happened to have a special dual 10K potmeter, with separate controls (outer and inner) for each section. If you use other resistor values, you have to recalculate the dividers. Make sure that the maximum output on the wiper of the output setting (R6) potmeter is slightly above 500mV to be able to drive the load to the maximum.

The wiper of the output setting potmeter goes to the switch that selects the static or dynamic behavior of the DC load.

To create a minimum offset for the DC load in the pulsed mode, used to test Transient Response and Recovery Time Measurements for power supplies, the other half of the dual 10K potmeter is used to add a 0..100mV offset. This should be enough to create a "bias" current of up to 1Amp for the supply, and then drive it higher with the pulse. This allows you to see the "sagging" and negative pulse excursions of the supply output voltage better than letting the supply go to ground, or 0V when you don't add the offset.

If you now turn on the dynamic input, the pulsed output level (with or without out offset) can be set, and the Amp level will be displayed on the Amp panel meter display.

Because I had this special dual potmeter, all I had to drill was a single new hole in the front panel, and that just sits just above the static output level potmeter. The circuit is build on a small prototype board that is mounted on an extended post already mounting the main PCB.

Everything works really well, this simple DC Load is getting better and better, I'm happy.

If you like what you see, please support me by buying me a coffee: https://www.buymeacoffee.com/M9ouLVXBdw