Warning: The magic method Restrict_Widgets::__wakeup() must have public visibility in /var/www/storepeter.dk/public_html/wp-content/plugins/restrict-widgets/restrict-widgets.php on line 72

Warning: Cannot modify header information - headers already sent by (output started at /var/www/storepeter.dk/public_html/wp-settings.php:589) in /var/www/storepeter.dk/public_html/wp-includes/feed-rss2.php on line 8
Camper – StorePeter https://storepeter.dk The world need chaos - I am here to help Mon, 23 Feb 2026 08:21:42 +0000 en-US hourly 1 https://wordpress.org/?v=7.0.1 Fra Sharon til Ferndale med Pop-Up Flyttelæs https://storepeter.dk/camper/fra-sharon-til-ferndale-med-pop-up-flyttelaes Sun, 05 Mar 2023 14:44:54 +0000 http://storepeter.dk/?p=5989 Continue reading ]]>  

Et af forårets projekter er at flytte det af vores habengut  (f.ex. min rundsav fra 1965) som ikke kan bruges i Danmark til Morfars hus i Ferndale CA, som bliver vores fremtidige sommer residens.

Niels Andreas og Far her, tager afsted i banditternes Spring Break, i vores mini-van med pop-up-trailer og kører på tværs af USA til Ferndale CA, En tur på minimum 3.218 miles (49 timer, 5.200 km). Denne gang kan vi skiftes til at køre.

Vi tog en tilsvarende tur på samme tid i 2021. Dengang var I-80 lukket, så vi blev nødt til at tage den nordlige passage via I-90 Montana/Washington, en omvej på 300 miles (500km) 5-6 timer.

Denne gang har vi tænkt os at tage en sydligere rute fra begyndelsen, I-40 syd om Las Vegas meget af turen går dermed der hvor HWY-66 gik i gamle dage

Som planen ser ud nu, tager vi afsted Fredag den 14. April når skolen er slut, og om alt går vel tager det 5 fulde dage at køre hele turen, så forhåbentlig vil vi være i Ferndale-CA Onsdag den 19. April.

Lørdag den 22. April flyver vi hjem til Boston, og efterlader bil og trailer i Ferndale, så har vi også en bil til sommerferierne.

Vi har tænkt os at overnatte på billige moteller, Da traileren og bilen jo sikkert er fyldt til bristepunktet.

Los Alamos

Når nu vi kører næsten lige forbi, er det oplagt at besøge Los Alamos.  Den mest effektive måde at gøre det på er nok at have Motel overnatninger et par timer på hver side, og så bruge dagen i Los Alomos, oplagte Motel byer er: Santa Rosa NM  og Grants NM

Men for nu ikke at gøre det for let er der faktisk to Museer:

Da jeg var forbi 1994 besøgte jeg sidste nævnte, jeg kendte ikke til det første, så hvilken skal det være?  eller skal det være begge ?

Direkte er der 195 miles 2:48 min mellem Santa Rosa og Grants NM, via Los Alamos bliver det 318 miles 5:30

Fra Sharon til Santa Rosa NM er der omkring 31 timers kørsel, hvilket burde kunne kalres på 3 dage

Fra Grants NM til Ferndale er der omkring 20 timers kørsel, dette burde kunne klare på 2 dage.

Vi starter Fredag efter skolens ophør og vi vil helst være fremme ved vores dagsmål før kl 21:00

]]>
Købingsmark til Horsens på cykel https://storepeter.dk/camper/kobingsmark-til-horsens-pa-cykel Tue, 29 Jun 2021 13:27:52 +0000 http://storepeter.dk/?p=5103 Continue reading ]]>

195km ,11 timer i sadlen ifølge brouter.de

Der er desvære ikke særligt mange banestier i Sønderjylland, så vi må nøjes med de små veje.

Købingsmark – Kalvø, 46 km

Kalvø – Sandersvig, 36 km

Sandersvig – Snoghøj, 46 km

Snoghøj – Ildved, 45 km

Ildved – Bryrup Banesti, 26 km

 

]]>
Horsens – Aalborg på Cykel via Banestier https://storepeter.dk/camper/horsens-aalborg-paa-cykel-via-banestier Wed, 16 Jun 2021 20:07:33 +0000 http://storepeter.dk/?p=4961 Continue reading ]]> For 40 år siden cyklede jeg næsten hvert år Danmark tyndt, Aalborg-Bajstrup tog 3 dage,  godt og vel 100km om dagen og en 5-6 timer i sadlen.  Det bedste var banestierne.

Jeg tror ikke jeg er i helt samme form som dengang, men det kommer vel. så i år vil vi tage turen fra sommerhuset til Aalborg via banestier/jernbanen

Der er masser af banestier, der er bøger om det, Udvalgte Jyske Banestier 2 kan læses online. Vi har også bogen i sommerhuset.  På deres hjemmeside lgbertelsen.dk/Banestier.html er der også printervenlige kort over de Banestier de beskriver i deres bøger.

railtrails.dk  har henvisninger til over 200 banestier i Danmark, med  Google Maps baserede kort

brouter.de er god til finde cykel-ruter, den er baseret på CyclOSM.org (Open Street Map) har  også banestier

Mange banestier har tilhørende primitive shelters hvor man kan overnatte. På udinaturen.dk kan man finde hvor de er.

Man kan næsten køre hele vejen fra Horsens til Aalborg på banestier så det vil vi gøre.  Det eneste stykke der mangler er mellem Spentrup og Havndal , men så kan vi jo lige tage et smut forbi Hvidsten Kro så 23km på små biveje klarer vi vel, selvom der sikkert er en enkelt bakke.

  • Cykel: Købingsmark – Rødekro 41km cyclOSM kort
  • Tog:    Rødekro – Horsens
  • Cykel: Horsens – Aalborg 220 km

Lund – Silkeborg 56 km

Bryrupbanen var en privatbane der blev nedlagt i 1968, og siden er den blevet til en helt fantastisk banesti, Sidste gang jeg cyklede på den var i 1981.

Silkeborg – Langå – Randers 56 km

Randers – Hadsund 44 km

Hadsund – Aalborg 54 km

Aalborg Hadsund Jernbane blev nedlagt i 1969, 25 km af banestien er fredet og der er flere steder undervejs hvor man kan overnatte i shelter, der er også multtoilet og vandhane

Turen tilbage Aalborg – Kolding

Hjemad finder vi lidt mere vestpå disse Banestier, vi tager nok toget når vi ikke gider mere.

 

]]>
Fra Sharon til Ferndale i campervan https://storepeter.dk/camper/fra-sharon-til-ferndale-i-campervan Thu, 18 Mar 2021 18:54:11 +0000 http://peter.storepeter.dk/?p=4814 Continue reading ]]> Endelig ser det ud til at vi kan komme ud og lufte vores campervan, det skal ske i banditternes Spring Break.  Vi er i denne sammenhæng Niels Andreas, Helene og Far her.  Vi kører på tværs af USA til Ferndale CA, hvor vi forventer at lande Tirsdag den 20. April,

Min smukke kone og Laure er på deres egen lille tur på vestkysten og de slutter sig til os dagen før i Ashland Oregon. Efter et par dage sammen på den venstre-kyst tager min smukke kone så  alle banditterne med hjem, mens Allie og jeg bliver i Ferndale og laver tegninger over Morfars hus.

Som planen ser ud nu tager vi afsted Lørdag den 10. April og forventer at ankomme til Moster Cindy i Odebolt Iowa efter et par dage, vi har før gjort den tur på 3 dage i en minivan.

Vi tager afsted ugen før “Spring Break” og dermed skal banditterne gå i skole som “COVID-normalt” (online via zoom). Så der bliver kun tid til at køre efter kl 2:00 pm, men vi håber vi kan komme et godt stykke der-ud-af i Week-enden. Forhåbentlig opdager lærerne ikke at de er “fraværende”.  Det er sådan set også sidste chance for at gribe det an på denne måde, da skolen går tilbage til almindelig klasse-værelse undervisning efter Spring Break – Man må tage de muligheder man får.

Jeg skulle måske lige nævne at både min smukke kone og jeg er færdig  vacciineret mod COVID-19 når vi tager afsted.  Selv Laure på 17 kan blive vaccineret for COVID-19 fra den 19 April.

Her er en link til google-maps over første del af turen.

Vi har tænkt os at overnatte på billige moteller, dels fordi det koster stort set det samme som Camping, og vi skal bruge deres Wifi så banditter kan gå i skole som “normalt”.

Vi har hundene med, vi regner med at de skal sove i campervan-en, men derfor er det nu alligevel smart at overnatte på hunde-venlige moteller. jeg har brugt www.tripswithpets.com til at finde moteller, og har notereret mig at:

Sharon MA – Odebolt IA

Turen fra Sharon til Iowa er beregnet til 22 timers kørsel, som jeg har inddelt i seks-en-halv etaper (ben) på 3-4 timer, og vi når vel 1-3 ben per dag.

  • 4 hr 44 min (311 mi) Syracuse NY
    Days Inn, 6609 Thompson Rd, Syracuse, NY 13206, $64
    Motel 6, NY 6577 baptist way, East Syracuse, NY, 13057, $51

  • 3 hr 37 min (237 mi) Erie PA
    Days Inn, 7415 Schultz Rd, Erie, PA 16509 $76
    Motel 6 7455 Schultz RD, Erie, PA 16509, $85

  • 3 hr 12 min (210 mi) Toledo OH
    Days Inn, 1704 Toll Gate Drive, Maumee, OH 43537 $42
    Motel 6, 5335 Heatherdowns Blvd – Toledo, OH 43614, $52

  • 3 hr 21 min (215 mi) East Chicago, IN
    Days Inn, 6201 Opportunity Ln, Merrillville, IN 46410 $66
    Motel 6, 8290 louisiana st, Merrillville, IN, 46410, $45

  • 3 hr 41 min (240 mi) Iowa City IA
    Travelodge, 2216 N Dodge St, Iowa City, IA 52245 $79
    Motel 6, 101 w. 65 st, Davenport, IA, 52806, $48

  • 2 hr 57 min (198 mi) Elk Horn, IA
    Tivoli Inn And Suites, 4037 Main St, Elk Horn, IA 51531 $80

  • 1 hr 12 min (59.8 mi) Odebolt, IA
    Odebolt, IA 51458

Ohio Turnpike Plaza

Ben 3 og 4 mellem Erie PA og East Chicago, går igennem Ohio og truckstops på Ohio Turnpike har dedikeret parkering for Mobile-Homes endda med hook-ups til strøm, man må blot ikke opholde sig over 24 timer på Turnpiken og deres raste-pladser. Jeg har lavet  en google map med disse stops der svarer til ben 3 og 4 ovenfor.

  • Erie PA som ovenfor
  • 2 hr 4 min (131 mi) Ohio Turnpike milepost 139, Middle Ridge Plaza
  • 58 min( 62.6 mi) Ohio Turnpike milepost 77, Blue Heron Plaza
  • 20 min(16.7 mi) Toledo som ovenfor
  • 49 min(49.2 mi) Ohio Turnpike milepost 20, Indian Meadow Plaza
  • 2 hr 38 min(166 mi) East Chicago IN som ovenfor

Odebolt IA – Ferndale CA

I Iowa vil vi besøge  Moster Cindy og Onkel Jeff, og vi vil også besøge Pete og Ashton og deres to banditter i South Dakota,  samtidig med at vore banditter skal passe deres skole.

Torsdag aften skulle vi gerne være ved Mount Rushmore så vi kan se monumentet oplyst.  Det er en Dansker Jørgen Børglum, der med en smule hjælp har lavet det. Fredag er banditternes sidste skoledag via zoom, – vi skal have lavet en opstilling hvor de stenede præsidenter er i baggrunden så vi kan imponere dem der hjemme.

Fra Mt Rushmore til Ashland Oregon er der 23 timers kørsel som jeg igen har delt i seks etaper af ca. 4 timer, og så vi skal nå seks etaper på 4 dage (f.ex. 2+1+1+2) turen kan ses på google maps.

Meget afhænger af hvilke pas der er åbne, det stadig vinter på toppen her i slutningen af April,

  • Cindy & Jeff Hansen, Odebolt, IA 51458 Mandag-> (Måske Tirsdag)

  • Wall Lake Park, 46290 266th St, Hartford, SD 57033
  • 3 hr 35 min (239 mi) Fedora SD 57337 >-Torsdag
    Pete & Ashton Hansen

  • Badlands National park 21020 SD-240, SD 57750
  • 4 hr 49 min (328 mi) Mt Rushmore SD Torsdag-Fredag
    Roosevelt Inn, 206 Cemetery Rd Keystone, SD 57751, $63, pool
    Baymont, 106 Highway 16A Keystone, SD 57751, $50, pool
    Holiday Inn, 321 Swanzey St, Keystone, SD 57751, $82, pool, no dogs

  • 3 hr 45 min (222 mi)
    Motel 6, 1150 Wilkins Cir, Casper, WY 82601, $40
    Travelodge, 2325 E Yellowstone Hwy – Casper, WY $55

  • 4 hr 41 min (280 mi) Fredag-Lørdag
    Motel 6, 600 US-89, Jackson, WY 83001, $86
    Antler Inn, 43 W. Pearl Ave, Jackson, WY, $85+$0

  • 4 hr 3 min (251 mi) Lørdag-Søndag
    Motel 6, 1472 Blue Lakes Blvd N, Twin Falls, ID 83301 $50
    SureStay Hotel by Best Western, 1260 Blue Lakes Boulevard N Twin Falls, ID 83301, $77+$10

  • 1 hr 51 min (124 mi) Søndag-Mandag
    Motel 6, 2323 W Airport Way, Boise, ID 83705, $59

  • 3 hr 7 min (188 mi)
    Days Inn, 577 W Monroe St, Burns, OR 97720, $74+$10
    OYO Hotel, 789 N Broadway Ave – Burns, OR, $74+$10

  • 5 hr 3 min (278 mi) Mandag-Tirsdag
    Ashland Springs Hotel 212 E Main St, Ashland, OR 97520

  • 4 hr 25 min (225 mi) Ferndale Tirsdag
    Francis Creek Inn, 577 Main St, Ferndale, CA 95536

 

Hvad skal vi undervejs ?

Off-Grid Camping og Seværdigheder

Campenium kan man blandt andet finde gratis camp-sites, så jeg har udvalgt nogen stykker som passer med 4 dages tur gennem Rokkebjergene på en rute lige syd om Yellowstone.. google maps for denne tur.

Denne tur er på 26 timer fra  Mt Rushmore til Ashland Oregon, og har hot springs, måne landskab, vandfald, og vulkan-krater (lukket indtl 1. Juni) og er nok tæt på den tur vi tager, måske tager vi et enkelt motel engang i mellem.

Ferndale CA – Sharon MA

Jeg har endnu ikke taget stilling til om jeg lader campervan-en blive i Ferndale og flyver hjem, eller om jeg kører hjem, måske en lidt sydligere tur via Grand Canyon, Memphis og Nashville.

]]>
Server Power supply DPS-750TB https://storepeter.dk/linux/server-power-supply-dps-750tb Tue, 07 Jul 2020 18:55:38 +0000 http://peter.storepeter.dk/?p=4491 Continue reading ]]> and how to get PMBus access from f.ex. a Raspberry Pi

My favorite power-supply for almost anything is the Server PSU for Dell R510/R710/R910. It is a 750w 12v 60amp supply with 80-plus-gold standard,  you can get them for $10-20 on ebay delivered.

People use these for battery chargers and there is a lot of info on server-power-supplies at https://www.rcgroups.com

I use these for Battery charger and for my 3D-printers. A nice feature is the standby power with which you can have a Raspberry Pi powered all the time, and use the RPI to turn the PSU on or off.

To use this PSU you need to activate the right pins. For the impatient reader try connecting pin29 and pin30 (and pin31 for i2c) to ground.  Or you could buy one of the break-out boards the bitcoin miners use to power their rig.

I  can recommend a breakout board from parallelminer.com, the X11 works fine with the DPS-750TB, I payed $10 delivered. it has a nice little voltmeter,  so well worth its price.

Among hackers the DPS-1200FB seem to be the most popular choice and hence a lot of information is available for this unit.  I have made a blog-post about it here Server Power supply DPS-1200FB.  Even if this is a different brand power-supply a lot information will apply to this PSU as well.

Below is links to information relevant to DPS-750TB:

The muRata D1U86T-W-800-12-HB4C looks very similar to the DPS-750TB same pin-layout, same short/long pins and as you will see later on in the post, many of the PMBus commands documented for muRata PSU work.  muRata has links to 3 application notes in its product notes:

The DPS-750TB was first introduced in 2009. SMBus 1.0 was introduced in 2005, so it makes sense to look at this older specification:

PMBus™ 1.0 Power System Management Protocol Specification:

Further information on the PMBus can found below:

Pinout for the DPS-750TB

I could not get the i2c connection working according to the pinout at rcgoups. So I have done a little reverse engineering  trying to find the i2c bus.

I have included my measurement below. I have measured: resistance  on the PSU unconnected, and voltages without the X11 and with the X11 from Parrallelminer, in the power off and power on state.

pinresistance
to GND
AC
no X11
off
w X11
on
w X11
rcgroup
pin
notes
1-121k4 0v 12v12v
13-24GND GNDGNDGND
25n.c. 0v0v0.12vtach
26203 0v3v30.05vRemoteSense-changed
275M3 3v40.3v3v3vin_goodchanged
2819k 0.23v3v30v2C-sharechanged by PSU
2917k 3v33v30v-PS_Onchanged by x11
30 short17k3v3 3v30vPS_Killchanged by X11
31 short98k3v3 3v33v3Reset0=enable i2c
32 short5M53v3 3v33v3Alert
33 short14k3v3 3v30v5sdachanged by X11
34 short99 ov0v0v-PS_Present
35 short14k 3v33v33v3scl
36GND GNDGNDGNDGND
3799k 0.01v0.008v3v3POKchanged
38980k 0.27v0.2v0.2vPS_A0
39 12v12v12v12V_SB12v_SB
40203
to 12v
0v0v12vRemoteSense+changed by PSU
41-52GNDGND GNDGND
53-641k4
12v12v

The X11  from Parrallel-miner controls 3 pins via two transistors

  • pin 33 can be pulled down to pin36 GND, does not seem to be necessary for this PSU. and makes i2c use impossible, they probably did this so the X11 can support other PSU as well.
  • pin 29 and pin 30 is tied together and can be pulled down to GND

The connections to the i2c was actually as described on rcgroups, but pin31 which is labeled reset on the rcgroups pinout, need to be pulled down to ground, for the PSU to show up on i2c_address 0x58.  It is worth noting that the muRata PSU uses the same i2c address as the DPS-750TB, but pin31 is unused, and not connected in the acan-84 application note. Another difference is pin25: on the DPS-750TB it sends pulses from the fan so its speed can be monitored, as you will see in the program at the end of this posting, this information is readily available via the PMBus., so not really needed.  Pin25 is called SMART_REDUNDANT on the muRata PSU.

Status

I can turn the PSU on and off from a small python script on a raspberry pi pulling PS_On low, Software on/off via the PMBus as described in the standard does not seem to work. 

My program  monitors the AC_ok, DC_ok pins, and the fan-speed,  by reading the tacho pin and also by reading PMBus register for fan-speed and PMBus registers for Input/Output Voltages/Currents/Power/Temperature. Below is how I have connected the RPI to the PSU.

PSU pinNameRPi gpioDescription
25Tacho22fan rpm pulse
27AC_ok27low when AC is good
37DC_ok17low when DC is good
29PS_On40/1 = On/Off
30PS_KillGNDvia 100R
31Enable_i2cGNDvia 1k
33SDAi2c_sda9k pullup to 3v3
35SCLi2c_sda9k pullup to 3v3
on/off-button23momentary microswitch

Raspberry Pi psu.py

The python program to monitor the PSU, reads pins my on/off button and a few PMBus registers and prints out the values, so the Dell DPS-750tb follow some kind of PMbus standard

Here is the output of my python script:


PMBus busID=0x01 addr=0x58 DPS-750TB connected

MFR_ID:       0FN1VTA00PS
MFR_MODEL:    DPS750TB1
MFR_REVISION: A0
PMBus revision: 0x00

AC_OK DC_OK fan_rpm: 1050 1696,  61 43 Celcius,  AC = 120 Volt,  DC 12.32 Volt  1.2 Amp In: 26 Out: 14 Watts on=0
AC_OK DC_OK fan_rpm: 1800 1696,  61 43 Celcius,  AC = 121 Volt,  DC 12.32 Volt  1.3 Amp In: 26 Out: 15 Watts on=0
AC_OK DC_OK fan_rpm: 1620 1696,  61 43 Celcius,  AC = 120 Volt,  DC 12.32 Volt  1.3 Amp In: 26 Out: 16 Watts on=0
AC_OK DC_OK fan_rpm: 1770 1696,  60 43 Celcius,  AC = 121 Volt,  DC 12.88 Volt  1.3 Amp In: 26 Out: 15 Watts on=0
AC_OK DC_OK fan_rpm: 1800 1696,  60 43 Celcius,  AC = 119 Volt,  DC 12.32 Volt  1.3 Amp In: 26 Out: 14 Watts on=0
AC_OK DC_OK fan_rpm: 1680 1696,  60 43 Celcius,  AC = 120 Volt,  DC 12.32 Volt  1.3 Amp In: 26 Out: 14 Watts on=0

The script is written in python3 (My first python script) and follows below, it builds on the PMBus library by Michael-Equi github.com/Michael-Equi/PMBus

#!/usr/bin/python3
# (C) storepeter peter@lorenzen.us 2020 BeerWare a'la phk
# -*- coding: utf-8 -*-
import sys
import RPi.GPIO as GPIO
import time
from pmbus import PMBus

# Pin configuration
PS_ON = 4
#PS_KILL = GND
DC_OK = 17
AC_OK = 27
TACH = 22       # Fan's tachometer output pin
ON_OFF = 23

def printf(format, *args):
    sys.stdout.write(format % args)

psu = PMBus(0x58
print("MFR_ID:       " + psu.MFR_ID())
print("MFR_MODEL:    " + psu.MFR_MODEL())
print("MFR_REVISION: " + psu.MFR_REVISION())
print("PMBus revision: 0x%02x\n" % psu.PMBus_revision())

# Setup GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull up to 3.3V
GPIO.setup(DC_OK, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(AC_OK, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(ON_OFF, GPIO.IN, pull_up_down=GPIO.PUD_UP)

on = 0
def on_via_hw(state):
    global on
    printf("on_via_hw on=%d, state=%d\n", on, state)
    on = state
    if state > 0:
        print("hwON")
        GPIO.setup(PS_ON, GPIO.OUT)
        GPIO.output(PS_ON,0)
    else:
        print("hwOFF")
        GPIO.setup(PS_ON, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pull up to 3.3V

# turn-on  states: 0=off <push>  1=off+button 2=on+button  <release> 3=on
# turn-off states: 3=on  <push> -1=on+button -2=off+buttib <release> 0=off
def check_on_switch():
    global on
    global ON_OFF

    if GPIO.input(ON_OFF)==0:   # button pushed
        if on == 0:         # not on - need to be pushed for a second
            on = 1
        elif on == 1:
            on_via_hw( 2)
        elif on == 3:       # was on
            on = -1
        elif on == -1:
            on_via_hw( -2)
    else:   # button release
        if on == 2:
            on = 3    # now it can be turned off again
        elif on < 0:
            on = 0    # now it can be turned on again

if len(sys.argv) > 1:
    if sys.argv[1] == "1":
        on_via_sw(3)
    else:
        on_via_sw(0)

count = 0
def falling(n):
    global count
    count = count + 1

GPIO.add_event_detect(TACH, GPIO.FALLING, falling)

prev = int(time.time())
try:
    while True:
        check_on_switch()
        t = int(time.time())
        if t != prev:  # every second
            prev = t
            rpm = 60 * count / 2       # 2pulses per revolution
            count = 0
            if GPIO.input(AC_OK): printf("AC_OK")
            if GPIO.input(DC_OK): printf(" DC_OK")
            printf(" fan_rpm: %4.f %d, ", rpm, psu.getFanSpeed())
            printf(" %d %d Celcius, ", psu.getTempurature(), psu.getTempurature2())
            printf(" AC = %d Volt, ", psu.getVoltageIn())
            printf(" DC %2.2f Volt ", psu.getVoltageOut())
            printf(" %2.1f Amp", psu.getCurrent())
            printf(" In: %d Out: %d Watts", psu.getPowerIn(), psu.getPowerOut())
            printf(" on=%d\n", on);

        time.sleep(0.1)

except KeyboardInterrupt: # trap a CTRL+C keyboard interrupt
    GPIO.cleanup() # resets all GPIO ports used by this function

The PMBus library by Michael-Equi github.com/Michael-Equi/PMBus seems to be written for a newer muRata power-supply, so I had to make a few changes to make it work with the DPS-750TB, which doesn’t seem to support Packet-Error-Checking, I have also added a number of routines to read more PSU register variables, the full library-file is below

# based on https://github.com/Michael-Equi/PMBus GPL-3
# changes by storepeter 2020 peter@lorenzen.us
from bitstruct import *
from smbus import SMBus
import sys
import math

class PMBus:

    #constants initialized on object creation
    VOUT_N = -9

    def __init__(self, addr, id=1, pecByte=False):
        self.busID = id
        self.address = addr
        self.pec = pecByte
        print("PMBus busID=0x%02x addr=0x%02x DPS-750TB connected\n" % (self.busID, self.address))

    #Decode/encode Linear data format => X=Y*2^N
    def delinear11(self, word):
        #print("\n" + format(word,'b'))
        u = unpack("s5s11", word.to_bytes(2, byteorder='big'))
        #print("%5s            = mantissa = %d" % (format(u[0] & 0x1f,'b'), u[0]))
        #print("     %11s = base     = %d" % (format(u[1],'b'), u[1]))
        result = u[1]*(2.0**(u[0]))
        #print("%4d * 2 ** %2d   = result   = %f" % (u[1], u[0], result))
        return result

    def delinear16(self, word):
        result = word*(2.0**self.VOUT_N)
        #print("%4d * 2 ** -9   = result   = %f" % (word, result))
        return result

    def readLinear(self, register):
        return self.delinear11(self._readWordPMBus(register))

    def _encodePMBus(self, message):
        YMAX = 1023.0
        #print(message)
        Nval = int(math.log(message/YMAX,2))
        #print("NVal: " + str(Nval))
        Yval = int(message*(2**-Nval))
        #print("YVal: " + str(Yval))
        message = ((Nval & 0b00011111)<<11) | Yval
        #print(bin(message))
        return message

    #wrapper functions for reading/writing a word/byte to an address with pec
    def _writeWordPMBus(self, cmd, word, pecByte=True):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        bus.write_word_data(self.address, cmd, word)
        bus.close()
    def _readWordPMBus(self, cmd, pecByte=True):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        data = bus.read_word_data(self.address, cmd)
        bus.close()
        return data

    def _writeBytePMBus(self, cmd, byte, pecByte=True):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        bus.write_byte_data(self.address, cmd, byte)
        bus.close()

    def _readBytePMBus(self, cmd, pecByte=True):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        data = bus.read_byte_data(self.address, cmd)
        bus.close()
        return data

    def readBlock(self, cmd, len):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        data = bus.read_i2c_block_data(self.address, cmd, len)
        bus.close()
        return data

    def readString(self, cmd):
        bus = SMBus(self.busID)
        if self.pec:
            bus.pec = self.pec
        len = bus.read_byte_data(self.address, cmd)
        data = bus.read_i2c_block_data(self.address, cmd, len)
        #print("data; %s" % str(data))
        data.pop(0)
        bus.close()
        return "".join(map(chr,(data)))

    ################################### Functions getting string info
    def PMBus_revision(self):
        return self._readBytePMBus(0x98)

    def MFR_ID(self):
        return self.readString(0x99)

    def MFR_MODEL(self):
        return self.readString(0x9a)

    def MFR_REVISION(self):
        return self.readString(0x9b)

    def MFR_LOCATION(self):
        return self.readString(0x9c)

    def MFR_DATE(self):
        return self.readString(0x9d)

    def MFR_SERIAL(self):
        return self.readString(0x9e)

    def getVoutMode(self):
        return self._readBytePMBus(0x20)

    def getVoutCommand(self):
        return self._readWordPMBus(0x21)

    def getVoutTrim(self):
        return self._readWordPMBus(0x22)

    def getVoutCal(self):
        return self._readWordPMBus(0x23)

    def getVoutMax(self):
        return self._readWordPMBus(0x24)

    def getVoutMarginHigh(self):
        return self._readWordPMBus(0x25)

    def getVoutMarginLow(self):
        return self._readWordPMBus(0x26)

    def getVoutTransitionRate(self):
        return self._readWordPMBus(0x27)

    def getVoutDroop(self):
        return self._readWordPMBus(0x28)

    def getVoltageScaleLoop(self):
        return self._readWordPMBus(0x29)

    def getVoltageScaleMonitor(self):
        return self._readWordPMBus(0x2a)

    ################################### Functions for setting PMBus values
    def setVinUVLimit(self, uvLimit, minUnderVolt=32.0):
        """The VIN_UV_WARN_LIMIT command sets the value of the input voltage that causes an
        input voltage low warning. This value is typically greater than the input undervoltage
        fault threshold, VIN_UV_FAULT_LIMIT (Section 15.27). The VIN_UV_FAULT_LIMIT
        command sets the value of the input voltage that causes an input undervoltage fault."""
        #min = 32, max = 75 on DRQ1250
        if(uvLimit > minUnderVolt):
            uvWarnLimit  = float(uvLimit) + 2
            uvFaultLimit = float(uvLimit)
        else:
            uvWarnLimit  = minUnderVolt + 2
            uvFaultLimit = minUnderVolt

        #print("Old VIN UV Limit: " + str(self.getVinUVLimit()))
        self._writeWordPMBus(0x59, self._encodePMBus(uvFaultLimit))
        self._writeWordPMBus(0x58, self._encodePMBus(uvWarnLimit))
        #print("New VIN UV Limit: " + str(self.getVinUVLimit()))

    def setVinOVLimit(self, ovLimit, maxOverVolt=110.0):
        """The VIN_OV_WARN_LIMIT command sets the value of the input voltage that causes an
        input voltage high warning. This value is typically less than the input overvoltage fault
        threshold. The VIN_OV_FAULT_LIMIT command sets the value of the input voltage that causes an
        input overvoltage fault."""
        #min = 32, max = 110 on DRQ1250
        if(ovLimit < maxOverVolt):
            ovWarnLimit  = float(ovLimit) - 2
            ovFaultLimit = float(ovLimit)
        else:
            ovWarnLimit  = maxOverVolt - 2
            ovFaultLimit = maxOverVolt

        #print("Old VIN OV Limit: " + str(self.getVinOVLimit()))
        self._writeWordPMBus(0x55, self._encodePMBus(ovFaultLimit))
        self._writeWordPMBus(0x57, self._encodePMBus(ovWarnLimit))
        #print("New VIN OV Limit: " + str(self.getVinOVLimit()))

    def setVoutOVLimit(self, ovLimit, maxOverVolt=15.6):
        """The VOUT_OV_WARN_LIMIT command sets the value of the output voltage at the
        sense or output pins that causes an output voltage high warning. This value is typically
        less than the output overvoltage threshold. The VOUT_OV_FAULT_LIMIT command sets the value
        of the output voltage measured at the sense or output pins that causes an output
        overvoltage fault."""
        #min = 8.1, max=15.6 on DRQ1250
        if(ovLimit < maxOverVolt):
            ovWarnLimit  = float(ovLimit) - 1
            ovFaultLimit = float(ovLimit)
        else:
            ovWarnLimit  = maxOverVolt - 1
            ovFaultLimit = maxOverVolt

        ovWarnLimit  = int(ovWarnLimit*(2**-self.VOUT_N))
        ovFaultLimit = int(ovFaultLimit*(2**-self.VOUT_N))

        #print("Old VOUT OV Limit: " + str(self.getVoutOVLimit()))
        self._writeWordPMBus(0x40, ovFaultLimit)
        self._writeWordPMBus(0x42, ovWarnLimit)
        #print("New VOUT OV Limit: " + str(self.getVoutOVLimit()))

    def setIoutOCLimit(self, ocLimit, maxOverCurrent=65.0):
        """The IOUT_OV_WARN_LIMIT command sets the value of the output current that causes
        an output overcurrent warning. The IOUT_OC_FAULT_LIMIT command sets the value of the output current, in
        amperes, that causes the overcurrent detector to indicate an overcurrent fault condition."""
        #min = 59, max = 65 for DRQ1250
        if(ocLimit < maxOverCurrent):
            ocWarnLimit  = float(ocLimit) - 3
            ocFaultLimit = float(ocLimit)
        else:
            ocWarnLimit  = maxOverCurrent - 3
            ocFaultLimit = maxOverCurrent

        #print("Old IOUT OC Limit: " + str(self.getIoutOCLimit()))
        self._writeWordPMBus(0x46, self._encodePMBus(ocFaultLimit))
        self._writeWordPMBus(0x4A, self._encodePMBus(ocWarnLimit))
        #print("New IOUT OC Limit: " + str(self.getIoutOCLimit()))

    def setIoutFaultResponse(self, byte):
        #see page 37-40 on PMBus spec for info on response bytes
        #print("Old IOUT Fault Response: " + bin(self.getIoutFaultResponse()))
        self._writeBytePMBus(0x47, byte)
        #print("New IOUT Fault Response: " + bin(self.getIoutFaultResponse()))

    def setOTLimit(self, otLimit, maxOverTemp=145.0):
        """The OT_WARN_LIMIT command set the temperature, in degrees Celsius, of the unit at
        which it should indicate an Overtemperature Warning alarm. The OT_FAULT_LIMIT command
        set the temperature, in degrees Celsius, of the unit at which it should indicate an Overtemperature Fault."""
        #min = 30, max = 145 for DRQ1250
        if(otLimit < maxOverTemp):
            otWarnLimit  = float(otLimit) - 3
            otFaultLimit = float(otLimit)
        else:
            otWarnLimit  = maxOverTemp - 3
            otFaultLimit = maxOverTemp

        #print("Old OT Limit: " + str(self.getOTLimit()))
        self._writeWordPMBus(0x4F, self._encodePMBus(otFaultLimit))
        self._writeWordPMBus(0x51, self._encodePMBus(otWarnLimit))
        #print("New OT Limit: " + str(self.getOTLimit()))

    def setFaultResponse(self, register, byte):
        #see page 37-40 on PMBus spec for info on response bytes
        """
        DRQ1250 registers:
        VIN UV  = 0x5A
        VIN OV  = 0x56
        VOUT OV = 0x41
        OT      = 0x50
        """
        print("Old Fault Response: " + bin(self.getFaultResponse(register)))
        return self._writeBytePMBus(register, byte)
        print("New Fault Response: " + bin(self.getFaultResponse(register)))

    def setTonDelay(self, delay):
        """The TON_DELAY sets the time, in milliseconds, from when a start condition
        is received (as programmed by the ON_OFF_CONFIG command) until the output
        voltage starts to rise."""
        #max delay is 500ms min is 1ms for DRQ1250
        self._writeWordPMBus(0x60, self._encodePMBus(delay))

    def setTonRise(self, time):
        """The TON_RISE sets the time, in milliseconds, from when the output starts to rise until
        the voltage has entered the regulation band."""
        #max time is 100ms, min is 10ms for DRQ1250
        self._writeWordPMBus(0x61, self._encodePMBus(time))


    def setToffDelay(self, delay):
        """The TOFF_DELAY sets the time, in milliseconds, from a stop condition
        is received (as programmed by the ON_OFF_CONFIG command) until the unit
        stops transferring energy to the output."""
        #max delay is 500ms, min is 0ms for DRQ1250
        self._writeWordPMBus(0x64, self._encodePMBus(delay))

    def setToffFall(self, time):
        """The TOFF_FALL sets the time, in milliseconds, from the end of the turn-off delay time
        (Section 16.5) until the voltage is commanded to zero. Note that this command can only be used
        with a device whose output can sink enough current to cause the output voltage
        to decrease at a controlled rate."""
        #max time is 100ms, min is 10ms for DRQ1250
        self._writeWordPMBus(0x65, self._encodePMBus(time))


    def storeUserAll(self):
        """The STORE_USER_ALL command instructs the PMBus device to copy the entire
        contents of the Operating Memory to the matching locations in the non-volatile User
        Store memory. Any items in Operating Memory that do not have matching locations in
        the User Store are ignored."""
        self._writeBytePMBus(0x15,0x00)

    def restoreUserAll(self):
        """The RESTORE_USER_ALL command instructs the PMBus device to copy the entire
        contents of the non-volatile User Store memory to the matching locations in the
        Operating Memory. The values in the Operating Memory are overwritten by the value
        retrieved from the User Store. Any items in User Store that do not have matching
        locations in the Operating Memory are ignored."""
        self._writeBytePMBus(0x16,0x00)

    def restoreDefaultAll(self):
        """The RESTORE_DEFAULT_ALL command instructs the PMBus device to copy the entire
        contents of the non-volatile Default Store memory to the matching locations in the
        Operating Memory. The values in the Operating Memory are overwritten by the value
        retrieved from the Default Store. Any items in Default Store that do not have matching
        locations in the Operating Memory are ignored."""
        self._writeBytePMBus(0x12,0x00)

    def clearFaults(self):
        """The CLEAR_FAULTS command is used to clear any fault bits that have been set. This
        command clears all bits in all status registers simultaneously. At the same time, the
        device negates (clears, releases) its SMBALERT# signal output if the device is asserting
        the SMBALERT# signal.The CLEAR_FAULTS does not cause a unit that has latched off for a
        fault condition to restart. Units that have shut down for a fault condition are restarted
        as described in Section 10.7."""
        self._writeBytePMBus(0x03,0x00)

    #See PMBus spec page 53-54 for information on the on/off functionality
    def regOff(self, hard=False):
        if hard:
            self._writeBytePMBus(0x01,0x00) #Hard off
        else:
            self._writeBytePMBus(0x01,0x40) #Soft off

    def regOn(self):	# no effect on dps-750-tb
        self._writeBytePMBus(0x01,0x80)

    ################################### Functions for getting PMBus values
    def getVoltageIn(self):
        return self.readLinear(0x88)

    def getVoltageOut(self):
        return self.delinear16(self._readWordPMBus(0x8B))

    def getCurrent(self):
        return self.readLinear(0x8C)

    def getPowerOut(self):
        return self.readLinear(0x96)

    def getPowerIn(self):
        return self.readLinear(0x97)

    def getTempurature(self):
        return self.readLinear(0x8D)

    def getTempurature2(self):
        return self.readLinear(0x8E)

    def getFanSpeed(self):
        return self.readLinear(0x90)

    def getVinUVLimit(self):
        #returns fault, warn
        return self.readLinear(0x59), self.readLinear(0x58)

    def getVinOVLimit(self):
        #returns fault, warn
        return self.readLinear(0x55), self.readLinear(0x57)

    def getVoutOVLimit(self):
        #returns fault, warn
        return self.deLinear16( self._readWordPMBus(0x40), self.deLinear16(self._readWordPMBus(0x42)

    def getIoutOCLimit(self):
        #returns fault, warn
        return self.readLinear(0x46), self.readLinear(0x4A)

    def getOTLimit(self):
        #returns fault, warn
        return self.readLinear(0x4F), self.readLinear(0x51)

    def getTonDelay(self):
        return self.readLinear(0x60)

    def getTonRise(self):
        return self.readLinear(0x61)

    def getToffDelay(self):
        return self.readLinear(0x64)

    def getToffFall(self):
        return self.readLinear(0x65)

    def getSwitchingFreq(self):
        #returns value in kHz
        return self.readLinear(0x95)

    def getDutyCycle(self):
        #returns value in %
        return self.readLinear(0x94)

    def getIoutFaultResponse(self):
        #see page 37-40 on PMBus spec for info on response bytes
        return self._readBytePMBus(0x47)

    def getFaultResponse(self, register):
        #see page 37-40 on PMBus spec for info on response bytes
        """
        DRQ1250 registers:
        VIN UV  = 0x5A
        VIN OV  = 0x56
        VOUT OV = 0x41
        OT      = 0x50
        """
        return self._readBytePMBus(register)

    #members for getting the status of the DRQ device
    #see PMBUS spec part two pages 77-79
    def getStatusSummary(self):
        """The STATUS_WORD command returns two bytes of information with a summary of the
        unit's fault condition. Based on the information in these bytes, the host can get more
        information by reading the appropriate status registers. The low byte of the STATUS_WORD
        is the same register as the STATUS_BYTE command."""
        # BUSY | OFF | VOUT_OV_Fault | IOUT_OC_FAULT | VIN_UV_FAULT | TEMPURATURE | CML (command memory logic) | None
        # VOUT Fault | IOUT Fault | POUT  Fault | INPUT Fault | MFR_Specific | PWR_GD | Fans | Other | Unknown
        # Note: if PWR_GD is set then pwr is not good (negative logic)
        self.statusSummary = self._readWordPMBus(0x79)
        status = {
            "busy" :          bool(self.statusSummary & (0b1<<7)),
            "off" :           bool(self.statusSummary & (0b1<<6)),
            "vout_ov_fault" : bool(self.statusSummary & (0b1<<5)),
            "iout_oc_fault" : bool(self.statusSummary & (0b1<<4)),
            "vin_uv_fault" :  bool(self.statusSummary & (0b1<<3)),
            "temp_fault" :    bool(self.statusSummary & (0b1<<2)),
            "cml_fault" :     bool(self.statusSummary & (0b1<<1)),
            "vout_fault" :    bool(self.statusSummary & (0b1<<15)),
            "iout_fault" :    bool(self.statusSummary & (0b1<<14)),
            "input_fault" :   bool(self.statusSummary & (0b1<<13)),
            "pwr_gd" :        not bool(self.statusSummary & (0b1<<11)),
            "fan_fault" :     bool(self.statusSummary & (0b1<<10)),
            "other" :         bool(self.statusSummary & (0b1<<9)),
            "unknown" :       bool(self.statusSummary & (0b1<<8)),
        }
        return status, self.statusSummary

i2c troubles

The program on the raspberry Pi keeps crashing due to i2c errors, so I got the scope out, and it seems that the PSU and the RPI does not drive it scl/sda with the same force, right now I have 9k ohm pullup on the lines, they probably need to be fine-tuned, or maybe I should try with an Arduino.

 

I looked at the output circuit of another server power-supply, which looked like this, I have no idea why this should be needed, or if this is the related to my troubles, I don’t even know if this circuit is used in this PSU.

i2c simple solution

in /boot/config.txt change the i2c line to

  • dtparam=i2c_arm=on,i2c_arm_baudrate=30000

 

]]>
Server Power supply DPS-1200FB https://storepeter.dk/linux/server-power-supply-dps-1200fb Mon, 06 Jul 2020 19:17:48 +0000 http://peter.storepeter.dk/?p=4568 Continue reading ]]> Among hackers the DPS-1200FB seem to be the most popular choice for a powerfull Server PowerSupply 12volt 100Amp for less than $20 delivered.

There is a lot of information available for this PSU.  So if you want to really get into controlling and monitoring the power-supply you should probably go for this one.  I have collected these links to projects and information about the DPS-1200-FB.

There is an i2c bus connection on these Server PSU used for PMBus, or som proprarity version of it, so lets see what that is all about

These powersupplies does NOT have a 80-plus-gold sticker, so they might be a little less efficient than the DPS-750TB that I normally use, or maybe HP just didn’t want to pay the extra cent to get the sticker – I might do power comparison some time in the future.

 

 

 

 

 

 

 

 

 

]]>
Camper Van https://storepeter.dk/camper/camper-van Fri, 06 Sep 2019 22:29:26 +0000 http://peter.storepeter.dk/?p=4274 Continue reading ]]> We have bought ourselves a camper van, it started with the idea that I would buy a little pop-up-camper, which you with some luck can pickup for $400 on craigslist, then I considered a normal camper trailer, small enough to be pulled by my Dodge Grand caravan, and finally we ended up buying a class-C motorhome.

We bought the one in the center

It was obvious that the camper needed a new roof, both the front and rear corner on the driver-side were soft and wet, but the price was right so we went for it.

]]>