User avatar
MarkHaysHarris777
Posts: 1820
Joined: Mon Mar 23, 2015 7:39 am
Location: Rochester, MN
Contact: Website

Cooling the Black Ice Zebra Case from C4Labs for PI3B

Tue May 24, 2016 2:51 am

My 5v 400ma GDT brushless fans came in the mails today. These small fans
are hard-working, low voltage, low current units designed for use in laptops;
50mm x 40mm x 10mm.

This fan was the chosen brushless for my Black Ice cases. I removed the clear plastic
insert around the [power, hdmi, Cvideo] port and then mounted the micro usb
power plug inside the fan housing. The power plug helps to hold the fan housing
in place. The fan moves about 5cfm, exhausting out the GPIO pinblock port.

In the following two pics, notice I have piggy-backed the 5v for the fan to the
Gertboard power pins.
brushless_fan2.jpg
brushless fan: 34c idle, 52c loaded four cores
brushless_fan2.jpg (53.49 KiB) Viewed 2779 times
brushless_fan.jpg
5v .4A brushless GDT fan for Black Ice case from C4Labs
brushless_fan.jpg (50.93 KiB) Viewed 2779 times
The fans are relatively quiet; but, they really work well. Both of these units pictured
idle without fans at about 46-47c, and neither unit could be driven hard (four cores each
running PI-calc) without 85c and crashing! With these squirrel cage fans
installed both units idle at about 34c and both units can be driven hard
(PI-calc four cores) with a top temp of only 52c. When the PI-calc(s) are
closed the temps quickly drop back to the high 30sc and eventually come back
to the 34c idle temp.

I'm happy with these small fans; they are low power, relatively quite, and they really do to job.
marcus
:ugeek:

KMyers
Posts: 33
Joined: Fri Nov 09, 2012 9:46 pm

Re: Cooling the Black Ice Zebra Case from C4Labs for PI3B

Tue May 31, 2016 7:27 pm

Looks good. Might be in my future project!

User avatar
MarkHaysHarris777
Posts: 1820
Joined: Mon Mar 23, 2015 7:39 am
Location: Rochester, MN
Contact: Website

Re: Cooling the Black Ice Zebra Case from C4Labs for PI3B

Sat Jun 04, 2016 4:25 am

Tonight I'm beginning to put some things together... its time to control the cooling fan with PWM; the idea (similar to most notebooks) is cool the device with a gentle breeze (low power low speed) until and if the unit gets too hot (I drive it hard) and then the fan speeds up to a momentary whirring until the temps drop and then its back to a gentle breeze...
fan_motor_control.jpg
open collector drivers in use on the Gertboard for fan motor speed control
fan_motor_control.jpg (56.98 KiB) Viewed 2352 times
From the pic board pin(35) is the PWM pin channel 1 that has been chosen to cool the PI... red wire in the center of the board... into buffered input (12), and then (12) output to the Relay1 RLY1 (yellow wire) to the open collector driver and then out to the fan motor (red and black). 5v is run from the Gertboard Vcc over to the PWR pin of the open collector (common) and the motor's ground return is furnished by the driver when it gets the signal from the Gertboards buffered output.

The fan motor speed control code is listed below; its just the first pass... just keeps the fan running at a nice even strain (very quite and quite efficient).

I set the frequency to 12Hz (can actually see the output LED flashing) with a 70% duty cycle. Again, its very hushed quiet, and I have the idle temps down to 34c (very nice). Its interesting (to me) that I can achieve the same results with PWM channel 0 pin(12) with a duty cycle of 60%; I don't understand the difference... just reporting the results.

The next phase is to add manual and automatic controls. I want to be able to manually set the fan speed up (or off) if I like, and I'd like the speed controller to monitor the temps of the PI and adjust itself... coming soon.

Also, in the final edition of the hardware I'm not going to dedicate a Gertboard to the process... I'll make my own small board for the purpose.

fan_motor_contorller.sh

Code: Select all

#!/usr/bin/python
#
# fan_motor_controller.sh
#
#  Mark H. Harris
#  v.01.a
#  06-03-2016
#

## Import the necessary header modules
from time import sleep
from sys import argv
import RPi.GPIO as GPIO

## Setup the GPIO for PWM on pin35
GPIO.setmode(GPIO.BOARD)
GPIO.setup(35, GPIO.OUT)
p = GPIO.PWM(35, 12)
p.stop()

def run_fan():
    p.start(70)
    while(1):
        sleep(10)

try:
    run_fan()
except KeyboardInterrupt:
    print(" ")
    print("motor controller ended by interrupt, bye!")
#except:
#    print(" ")
#    print("unmonitored exception in fan_motor_controller()")
finally:
    p.stop()
    GPIO.cleanup()
marcus
marcus
:ugeek:

User avatar
MarkHaysHarris777
Posts: 1820
Joined: Mon Mar 23, 2015 7:39 am
Location: Rochester, MN
Contact: Website

Re: Cooling the Black Ice Zebra Case from C4Labs for PI3B

Sat Jun 04, 2016 8:17 am

double_fan_control.jpg
Dual fan_motor_controller using UNL2803 (Gertboard)
double_fan_control.jpg (52.19 KiB) Viewed 2274 times
Both of my primary development PIs (rasppi3 & rasppi4) are now running cool with fan_motor_controller(s) and the controls (signals) are now in place and functioning well. In the final setup I think I will provide another wall-wart for fan power; however, both of these fans are being operated from the 5v Vcc of rasppi3. Each output of the UNL2803 can take 50v and up to 500ma ! I am giving them 5v and something less than 300ma... From the pic, I'm using buffer (12) relay (1) for rasppi3 and buffer (3) and relay (3) for rasppi4. I increased the freq to 18Hz, and am running the duty cycle between 95 High, and 65 Low. On (Low) both units idle at 34c. (without fan idle is @ 48c)

The UNL2803 cost $1.95 from Sparkfun; they are sooo much better than a transistor or fet, and more cost effective. -- I'll probably just by one for each PI, and have five spare open collector drivers for each! Then I can power the fans with the PI Vcc without troubles.

The power (High Low) may be toggled by sending the process (running in the background &) a HUP signal; a USR1 signal (under script control) will set the power High, and a USR2 signal under script control will reset the power Low. A separate script is going to be used to monitor the temps and send the signals (and keep the logs)... that's the unix way.

Below I have included the updated fan_motor_controller.sh, as well the fansig.sh.

fan_motor_controller.sh v.01c

Code: Select all

#!/usr/bin/python
#
# fan_motor_controller.sh
#
#  Mark H. Harris
#  v.01e
#  06-05-2016
#

## Import the necessary header modules
import time as TIME
from sys import argv
import signal as SIGNAL
import RPi.GPIO as GPIO

## Setup the GPIO for PWM on pin12
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 18)
p.stop()

duty_cycle = 65
hup_flag = False

def run_fan(duty):
    p.start(duty)
    while(1):
        TIME.sleep(30)
        p.ChangeDutyCycle(duty_cycle)

def ssighup(signum, frame):
    global hup_flag
    global duty_cycle
    if (hup_flag):
        duty_cycle = 65
        hup_flag=False
    else:
        duty_cycle = 95
        hup_flag=True
    print(" ")
    print("HUP: duty_cycle fan power toggled")

def ssigusr1(signum, frame):
    global hup_flag
    global duty_cycle
    if (duty_cycle<95):
        duty_cycle += 5
    print(" ")
    print("USR1: duty_cycle fan power set "+str(duty_cycle))

def ssigusr2(signum, frame):
    global hup_flag
    global duty_cycle
    if (duty_cycle>45):
        duty_cycle -= 5
    print(" ")
    print("USR1: duty_cycle fan power set "+str(duty_cycle))

SIGNAL.signal(SIGNAL.SIGHUP, ssighup)
SIGNAL.signal(SIGNAL.SIGUSR1, ssigusr1)
SIGNAL.signal(SIGNAL.SIGUSR2, ssigusr2)

try:
    run_fan(90)
except KeyboardInterrupt:
    print(" ")
    print("motor controller ended by interrupt, bye!")
#except:
#    print(" ")
#    print("unmonitored exception in fan_motor_controller()")
finally:
    p.stop()
    GPIO.setup(12, GPIO.OUT)
    GPIO.output(12, True)
    GPIO.cleanup()

fansig.sh

Code: Select all

#!/bin/bash
echo
PWMPID=$(ps ax |grep fan_motor |grep -v grep |awk '{print $1}' )
echo "PID: $PWMPID"
if [ "$PWMPID" != "" ]
  then
    if [[ "$1" != "HUP" && "$1" != "INT" && "$1" != "USR1" && "$1" != "USR2" ]]
      then
        echo "Error: valid signals are HUP INT USR1 USR2"
      else
        exec kill -SIG$1 $PWMPID
    fi
  else
    echo "PWM_counter.sh does not appear to be running"
fi

marcus
marcus
:ugeek:

User avatar
MarkHaysHarris777
Posts: 1820
Joined: Mon Mar 23, 2015 7:39 am
Location: Rochester, MN
Contact: Website

Re: Cooling the Black Ice Zebra Case from C4Labs for PI3B

Fri Jun 10, 2016 8:56 am

The fan motor controller is now fully automated with the addition of a script that monitors the RPi temperature and signals the motor controller to engage high speed until the temperature drops. In addition the temperature monitor doubles (primarily) as a binary temperature display. The temperature display has two modes (bar-graph & binary decimal) selectable via interrupt signal. When the temperatures exceed 55 degrees celcius the scipt RPi_tempLED.sh signals the fan_motor.sh script to increase the motor speed. Subsequently when the temperature falls (at or below 45c) the motor controller is signaled to reduce fan speed.
bar_graph_temp.jpg
RPi_tempLED.sh used as a monitor to display temperature in a bar-graph
bar_graph_temp.jpg (46.91 KiB) Viewed 2138 times
In the pic above I have the fan_motor.sh controller started as process 3927. I have started four PI-calc processes in the background which immediately drives the cpu temperature up. When the temperature hits 55c RPi_tempLED.sh signaled fan_motor.sh (via ssendsig.sh) with a HUP and the fan power was toggled high ! This worked so well, actually, it surprised me. Also from the pic, the display defaults to a bar-graph (cool temps in green, hot temps in red) from right to left, bottom to top. With the fan on high the red bar never went beyond 1 light. The graph represents a proportional range from 34c to 75c.
hex_graph_temp.jpg
SIGUSR1 has been used to toggle the display to binary-decimal
hex_graph_temp.jpg (42.07 KiB) Viewed 2138 times
In this pic above I have sent the RPi_tempLED.sh script a SIGUSR1 via ssendsig.sh in order to toggle the display to binary-decimal. The binary-decimal display uses hex digits (in binary) 0-9 to display the temperatures in decimal! The diplay pictured is showing 56c (digit 5 in the red nibble, digit 6 in the green nibble) which is the rounded equivalent of the 55.8c returned from the signal handler showing the output from the vcgencmd.

I'll be able to know whether my robot is overheating just by looking at its chest plate! The temperature monitor is display pattern (13). The other display patterns will be used eventually to display other system information including battery levels, gps, IP data, etc. At the moment HUP will exit the monitor and return to pattern 1, and USR2 returns temperature information only but does not change the motor controller, nor the monitor. The monitor uses ssendsig.sh itself to signal the fan_motor.sh controller script. This functionality is provided by the Python subprocess module call and check_output.

The codes are listed below. fan_motor.sh was previously listed above.


display8bit.py

Code: Select all

## display8bit.py
#
#   Mark H. Harris
#   v.01h
#   06-10-2016
#   Rochester, MN
#
#
## IMPORTS
from time import sleep
from sys import argv
import RPi.GPIO as GPIO

## ALL GPIO use Broadcom Numbering
GPIO.setmode(GPIO.BCM)

## PUSH BUTTON SWITCH DEFINED HERE, BUT NOT ALWAYS USED
p_switch = 19
GPIO.setup(p_switch, GPIO.IN, pull_up_down=GPIO.PUD_UP)

## BINARY ROMS 
red_pins=[23, 22, 21, 20]
for pin in red_pins:
    GPIO.setup(pin, GPIO.OUT)

green_pins=[27, 26, 25, 24]
for pin in green_pins:
    GPIO.setup(pin, GPIO.OUT)

## FUNCTION DEFINITIONS
def push_button():
    if GPIO.input(p_switch):
        return False
    else:
        return True

def led_on(pin):
    GPIO.output(pin, True)

def led_off(pin):
    GPIO.output(pin, False)

def bin_display(val, led_rom, nibble, std_inv):
    bin_digit = nibble
    if (std_inv=="STD"):
        start=0; finish=4; incrmt=1
    else:
        start=3; finish=-1; incrmt=-1
    for n in range(start, finish, incrmt):
        if (val & bin_digit):
            led_on(led_rom[n])
        else:
            led_off(led_rom[n])
        bin_digit = bin_digit<<(1)

def dsp8_STD(val):
    bin_display(val, red_pins, 0x10, "STD")
    bin_display(val, green_pins, 0x1, "STD")

def dsp8_INV(val):
    bin_display(val, green_pins, 0x10, "INV")
    bin_display(val, red_pins, 0x1, "INV")

def counter_8bit(max, t_delay, std_inv):
    for n in range(max):
        if (std_inv=="STD"):
            dsp8_STD(n)
        else:
            dsp8_INV(n)
        sleep(t_delay)

def counter_4bit(max, led_rom, t_delay, std_inv):
    for n in range(max):
        bin_display(n, led_rom, 0x1, std_inv)
        sleep(t_delay)

def walking(cycles, t_delay, std_inv):
    for m in range(cycles):
        digit=0x1
        for n in range(9):
            if (std_inv=="STD"):
                dsp8_STD(digit) 
            else:
                dsp8_INV(digit) 
            sleep(t_delay)
            digit = digit << (1)

def all_off():
    dsp8_INV(0x0)

def all_on():
    dsp8_INV(0xff)

def end():
    GPIO.cleanup()
    quit()

RPi_tempLED.sh

Code: Select all

#!/usr/bin/python
# RPi_tempLED.sh
#
#   Mark H. Harris
#   v.01h
#   06-12-2016
#   Rochester, MN
#
## REQUIRES v.10h display8bit.py
import display8bit as DSP
import signal as SIG
from subprocess import call as CALL
from subprocess import check_output as CKO

## RUN CYCLE COUNTER DISPLAYED WITH HUP SIGNAL
cycle = 0

## EXIT FLAG FROM SIGINT INTERRUPT HANDLER
interrupt_flag = False

## USER FLAGS USED BY SIGUSR1 & SIGUSR2
#     commands used by subprocess check_output and call
VCGENCMD=["/opt/vc/bin/vcgencmd","measure_temp"]
READTEMP=["cat","/sys/class/thermal/thermal_zone0/temp"]
FANMCMD=["ssendsig.sh","fan_motor","HUP"]
#     temperature display delay timer
t_temp_delay = 2.5
#     constants used with function (13) temperature monitor
TEMPS=1
TEMPBAR=0
TEMPDEC=1
#     flags used for temperature control
temp_flag=TEMPBAR
fan_flag=False
user_flag=0

## DISPLAY ORDER OF PATTERNS USED BY SIGUSR1 AND SIGUSR2
#      fan and temperature control (13)
#          other functions placeholders, but they do display !
#
led_pattern_index=[2, 12, 1, 4, 3, 9, 11, 5, 7, 13, 6, 8, 0, 10]

## BEGINNING DISPLAY PATTERN (IN THIS EXAMPLE 13)
#     led_pattern_index numbered from 0
#         pattern_index = 9
#         led_pattern_index[2] = 13  (see index above)
pattern_index=9
pattern_num = led_pattern_index[pattern_index]

## FREQUENCY SETTINGS (DELAYS) SET BY HUP
tt_delay_val=[0.023, 0.077, 0.140, 0.258, 0.70]
tt_delay_index=2
tt_delay=tt_delay_val[tt_delay_index]

## DISPLAY LED PATTERN ROMS
bar_temp_leds = [0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0xff]
circular2R = [0x88, 0xc0, 0x60, 0x30, 0x11, 0x03, 0x06, 0x0c]
dual_walker= [0x81, 0x42, 0x24, 0x18, 0x24, 0x42]
side_side_steps = [0x08, 0x40, 0x02, 0x10, 0x01, 0x20, 0x04, 0x80]
figure_eight = [0x80, 0x40, 0x02, 0x01, 0x10, 0x20, 0x04, 0x08]
two_led_wave = [0x88, 0x44, 0x22, 0x11, 0x22, 0x44]
blinker = [0x10, 0x01, 0x0, 0x0]
all_leds_on_flasher = [0xff, 0x00]
green_red_bars_flasher = [0xf0, 0x0f]
alternating_flasher = [0x69, 0x96]
curtain_scanner = [0xf, 0x8f, 0xc7, 0xe3, 0xf1, 0xf0, 0xf1, 0xe3, 0xc7, 0x8f]

## SIGUSR2 HANDLER FOR SELECTING PREVIOUS DISPLAY INDEX
def ssigusr2(signum, frame):
    global pattern_num
    global pattern_index
    if (user_flag==TEMPS):
        print("\n "+CKO(VCGENCMD))
        print("\n USR2: temperature information only")
    else:
        if (pattern_index>0):
            pattern_index-=1
        else:
            pattern_index=len(led_pattern_index)-1
        pattern_num=led_pattern_index[pattern_index]
        print("\n USR2: swapping backward to previous pattern: "+str(pattern_num)+"\n")

## SIGUSR1 HANDLER FOR SELECTING NEXT DISPLAY INDEX
def ssigusr1(signum, frame):
    global pattern_num
    global pattern_index
    global temp_flag
    if (user_flag==TEMPS):
        print("\n "+CKO(VCGENCMD))
        if (temp_flag==TEMPDEC):
            temp_flag=TEMPBAR
        else:
            temp_flag=TEMPDEC
        print("\n USR1: temperature LED display toggled")
    else:
        if (pattern_index<len(led_pattern_index)-1):
            pattern_index+=1
        else:
            pattern_index=0
        pattern_num=led_pattern_index[pattern_index]
        print("\n USR1: swapping forward to next pattern: "+str(pattern_num)+"\n")

## SIGHUP HANDLER FOR DISPLAYING INFORMATION AND ROTATING DELAY INDEX
def ssighup(signum, frame):
    global tt_delay
    global tt_delay_index
    global pattern_num
    global pattern_index
    global user_flag
    if (user_flag==TEMPS):
        user_flag=0
        pattern_index=2
        pattern_num=led_pattern_index[pattern_index]
    else:
        tt_delay_top = len(tt_delay_val)-1
        if (tt_delay_index<tt_delay_top):
            tt_delay_index+=1
        else:
            tt_delay_index=0
        tt_delay=tt_delay_val[tt_delay_index]
        print("\n cycle: "+str(cycle))
        print("\n delay: "+str(tt_delay))
        print("\n HUP: presently displaying pattern: "+str(pattern_num)+"\n")

## SIGINT HANDLER FOR EXITING THE DISPLAY PROCESS
def ssigint(signum, frame):
    global interrupt_flag
    print("\n keyboard interrupt"+"\n")
    interrupt_flag = True

## SIGNAL TRAPS
SIG.signal(SIG.SIGINT, ssigint)
SIG.signal(SIG.SIGHUP, ssighup)
SIG.signal(SIG.SIGUSR1, ssigusr1)
SIG.signal(SIG.SIGUSR2, ssigusr2)

## MAP FUNCTION USED BY READ_BAR_TEMP()
def pMap(x, in_min, in_max, out_min, out_max):
    return int((x-in_min) * (out_max-out_min) / (in_max-in_min) + out_min)

## DISPLAY FUNCTION FOR READING AND RESOLVING DISPLAY ROMS INFORMATION
def displayf(pattern, fwd_rvs):
    p_pattern=[]
    if (fwd_rvs=="RVS"):
        for n in range(len(pattern)-1, -1, -1):
            p_pattern.append(pattern[n])
    else:
        p_pattern=pattern
    for p_code in p_pattern:
        DSP.dsp8_STD(p_code)
        DSP.sleep(tt_delay)

## FUNCTION FOR RETURNING CPU|GPU TEMPERATURE (hex)
def read_hex_temp():
    hex_digits=list(str(int(round(float(CKO(READTEMP))/1000))))
    return int(hex_digits[0])*16 | int(hex_digits[1])

## FUNCTION FOR RETURNING CPU|GPU TEMPERATURE (decimal)
def read_bar_temp():
    return int(round(float(CKO(READTEMP))/1000))

## display LED function (decimal)
def dec_temp(value):
    DSP.dsp8_STD(0x0)
    DSP.sleep(.258)
    DSP.dsp8_STD(value)
    DSP.sleep(t_temp_delay)

## display LED function (hex)
def bar_temp(value):
    DSP.dsp8_STD(0x0)
    DSP.sleep(.258)
    DSP.dsp8_STD(bar_temp_leds[pMap(value, 32, 75, 0, 8)])
    DSP.sleep(t_temp_delay)
    

## MAIN TRY BLOCK FOR THE DISPLAY PROCESS
while (not interrupt_flag):
    try:
        if (pattern_num==0):
            ## 0 FLASHING BARS OF GREEN AND RED
            displayf(green_red_bars_flasher, "FWD")
        if (pattern_num==1):
            ## 1 DUAL WALKING PATTERN SIMPLE
            displayf(dual_walker, "FWD")
        if (pattern_num==2):
            ## 2x2 ALTERNATE FLASHING PATTERN SIMPLE
            DSP.dsp8_STD(0xcc)
            DSP.sleep(tt_delay)
            DSP.dsp8_STD(0x33)
            DSP.sleep(tt_delay)
        if (pattern_num==3):
            ## 3 SINGLE RAPID WALKER FROM DSP
            DSP.walking(1, tt_delay, "STD")
            DSP.sleep(tt_delay)
            DSP.walking(1, tt_delay, "INV")
            DSP.sleep(tt_delay)
        if (pattern_num==4):
            ## 4 STEPS BACK-AND-FORTH LEFT-AND-RIGHT
            displayf(side_side_steps, "FWD")
            DSP.dsp8_STD(0x0)
            DSP.sleep(tt_delay)
            displayf(side_side_steps, "RVS")
            DSP.dsp8_STD(0x0)
            DSP.sleep(tt_delay)
        if (pattern_num==5):
            ## 5 CLOCK-WISE 2-LED CIRCULAR WALKING D-RIGHT
            displayf(circular2R, "FWD")
        if (pattern_num==6):
            ## 6 COUNTER CLOCK-WISE 2-LED CIRCULAR WALKING D-LEFT
            displayf(circular2R, "RVS")
        if (pattern_num==7):
            ## 7 FIGURE EIGHT X1 WALKING
            displayf(figure_eight, "FWD")
        if (pattern_num==8):
            ## 8 FIGURE EIGHT X1 WALKING
            displayf(figure_eight, "RVS")
        if (pattern_num==9):
            ## 9 TWO LED WAVE DISPLAY   
            displayf(two_led_wave, "FWD")
        if (pattern_num==10):
            ## 10 ALL LEDS ON FLASHER !  
            displayf(all_leds_on_flasher, "FWD")
        if (pattern_num==11):
            ## 11 ALTERNATING PATTERN INTERESTING  
            displayf(alternating_flasher, "FWD")
        if (pattern_num==12):
            ## 12 ALTERNATING PATTERN INTERESTING  
            displayf(curtain_scanner, "FWD")
        if (pattern_num==13):
            ## 13 TEMPERATURE MONITOR AND FAN MOTOR CONTROL  
            user_flag=TEMPS
            b_temp = read_bar_temp()
            if (b_temp>=54 and not fan_flag):
                fan_flag=True
                CALL(FANMCMD)
            if (b_temp<=45 and fan_flag):
                fan_flag=False
                CALL(FANMCMD)
            if (temp_flag==TEMPDEC):
                dec_temp(read_hex_temp())
            if (temp_flag==TEMPBAR):
                bar_temp(b_temp)
    finally:
        if interrupt_flag:
            break
        cycle+=1
        # print("cycle: "+str(cycle)+"\n") DSP.all_off()

DSP.GPIO.cleanup()

ssendsig.sh

Code: Select all

#!/bin/bash
echo
PWMPID=$(ps ax |grep -i $1 |grep -v grep |grep -v sudo |grep -v ssendsig |awk '{print $1}' )
echo "PID: $PWMPID"
if [ "$PWMPID" != "" ]
  then
    if [[ "$2" != "HUP" && "$2" != "INT" && "$2" != "USR1" && "$2" != "USR2" ]]
      then
        echo "Error: valid signals are HUP INT USR1 USR2"
      else
        exec kill -SIG$2 $PWMPID
    fi
  else
    echo "PWM_counter.sh does not appear to be running"
fi

marcus
marcus
:ugeek:

User avatar
MarkHaysHarris777
Posts: 1820
Joined: Mon Mar 23, 2015 7:39 am
Location: Rochester, MN
Contact: Website

Re: Cooling the Black Ice Zebra Case from C4Labs for PI3B

Tue Jul 26, 2016 5:49 pm

Good afternoon everyone, I have simplified the active cooling solution for the Zebra Case Black Ice for Raspberry PI; this in response to the idea that the motor might be driven nicely by a single transistor--- and in fact it can be !

I now have single transistor (PN2222 or 2N2222) motor controllers on all my PIs, as well on my PineA64 board. The pic below is Rasppi3 using the 5v brushless fan from Gdt...
fan_5V_ctrl_PI3B.jpg
raspberry PI with PN2222 motor controller
fan_5V_ctrl_PI3B.jpg (56.47 KiB) Viewed 1844 times
I have attached the python codes below. This same code is running on all my PIs and the PineA64 boards, using software pwm to provide a low speed duty cycle (16ms on, 4ms off) at around 50pps. This keeps the fan quiet , moves the air efficiently , and allows for controlling the speed as well on|off with simple GPIO on pin(12).

The pic below is a close-in of the transistor driver on a mini-breadboard:
Fan_driver_2N2222.jpg
PN2222 transistor driver fan motor speed controller
Fan_driver_2N2222.jpg (57.63 KiB) Viewed 1844 times
The 5v pin goes to the transistor collector, while the emitter goes to ground. The GPIO18 pin(12) goes to the transistor base via 1000 ohm resistor. The transistor is pulsed on|off by the python codes (see below) to keep the fan running smoothly and quietly, while also allowing it to be controlled via signal (HUP) while running.

Sending the process a -SIGHUP will toggle the duty cycle from 80% to 100% (low vs high speed) and (obviously) the motor can be stopped under program control by sending a -SIGINT.

fan_motor.sh

Code: Select all

#!/usr/bin/python
#
# fan_motor.sh
#
#  Mark H. Harris
#  v.01b
#  07-25-2016
#

## Import the necessary header modules
from time import sleep
import signal as SIGNAL
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

## SOFTWARE PWM GPIO18 pin(12)
soft_pwm = 18
GPIO.setup(soft_pwm, GPIO.OUT)

ms_on = .016
ms_off = .004
hup_flag = False

## FUNCTION DEFINITIONS

def motor_duty_on(m_pin, t_delay):
    GPIO.output(m_pin, True)
    sleep(t_delay)
    
def motor_duty_off(m_pin, t_delay):
    GPIO.output(m_pin, False)
    sleep(t_delay)

def end():
    GPIO.cleanup()
    quit()

def ssighup(signum, frame):
    global hup_flag
    global ms_on
    global ms_off
    if (hup_flag):
        ms_on = .016
        hup_flag=False
    else:
        ms_on = .250
        hup_flag=True
    print(" ")
    print("HUP: duty_cycle toggled value: "+str(ms_on))

SIGNAL.signal(SIGNAL.SIGHUP, ssighup)

kb_interrupt = False

while(not kb_interrupt):
    try:
        motor_duty_on(soft_pwm, ms_on)
        motor_duty_off(soft_pwm, ms_off)
    except KeyboardInterrupt:
        kb_interrupt = True
        print(" ")
        print("motor controller ended by interrupt, bye!")

end()

cheers,
marcus
marcus
:ugeek:

Return to “Other projects”