#!/bin/sh
# Designed to be polled slowly as a cron job, ie every few minutes.
# Checks the k8055 card/port 0, if any,
# and writes the input values to a k8055.out file
# or removes it if not available.
# Also transfers the current status of the output.flags/[0-7].flag
# values to the digital outputs.
# If a numeric flag (starts 0. to 7.) is present then the output is turned off
# (except as adjusted by the modifiers table if present,
# which may also enforce minimum on/off times, etc).
# Unless the input voltage as measured at A1 is acceptably high
# ie the battery has a reasonable state of charge
# then this sets a EXTERNAL_BATTERY_LOW.flag for use by various systems
# and forces a sync to storage to help minimise data loss if power fails
# and tries to turn down power in some other subsystems.
# This flag is set by default as a precaution if monitoring in not possible.
#
# We also set the SheevaPlug blue light to off for low/unknown.
# heartbeat for normal,
# steady for high.
# On any significant change in external power availability
# between one sample and the next this may set the EXT_POWER_CHANGE.flag
# to help keep the system stable.
# Directory to set/clear flags in.
# Should be readable by everyone, and writable by this script,
# but not writable by everyone.
WORKDIR=/var/run
# Log external battery state-of-charge (SOC) good flag (has been over float).
BATTGOOD=${WORKDIR}/EXTERNAL_BATTERY_GOOD.flag
# Log external battery state-of-charge (SOC) warning flag.
BATTWARN=${WORKDIR}/EXTERNAL_BATTERY_LOW.flag
# Log external battery state-of-charge (SOC) warning flag.
BATTVLOW=${WORKDIR}/EXTERNAL_BATTERY_VLOW.flag
# External flag set if PV generation forecast is good for next 24h+.
# Eg prime from weather forecast for next day...
#
Wednesday: Thick Cloud, Maximum Temperature: 15°C (59°F) Minimum Temperature: 13°C (55°F)
#47 11 * 11,12,1 * rm /var/run/FORECAST_PV_GEN_GOOD.flag
#47 6,11,21 * 2-10 * if [ "sunny" != "`wget -q -O - http://open.live.bbc.co.uk/weather/feeds/en/kt1/3dayforecast.rss | awk '/.*emperature/ { if(++count==2) {if($0 ~ /[Ss]unny/){printf("sunny");}exit;} } '`" ] ; then rm -f /var/run/FORECAST_PV_GEN_GOOD.flag; else touch /var/run/FORECAST_PV_GEN_GOOD.flag; fi
FORECASTGENGOOD=${WORKDIR}/FORECAST_PV_GEN_GOOD.flag
# If the '-nolog' flag is set, we do not log the data sample.
# This can be useful during testing for example.
NOLOG="false"
if [ "X$1" = "X-nolog" ]; then
NOLOG="true"
shift
elif [ -e $BATTVLOW ]; then
if [ "X`find $BATTVLOW -mmin -9`" != "X" ]; then
# When very short of energy we avoid doing expensive logging too often.
# Forbid a re-run in much less than 9 minutes.
exit 0
fi
fi
# Directory of flags to convey to the k8055 board's digital outputs.
# Note that flag zero is ignored and generated internally below.
OUTBITSDIR=/local/k8055/outbits
# Bit map of flags; default is all off.
outbitsValue=0
# SPECIAL CASE FOR BIT 0 FOR EFFICIENCY.
# Set bit/output 0 high if battery status has been good for >>1h, else clear.
# Use to turn on external dump loads such as ADSL/modem.
if [ -f $BATTGOOD ] && [ ! -z "`find $BATTGOOD -mmin +71`" ]; then
outbitsValue=1
fi
# Assemble output word to send to output latch while collecting input data.
#if [ -e "$OUTBITSDIR/1.flag" ]; then outbitsValue=`/usr/bin/expr 2 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/2.flag" ]; then outbitsValue=`/usr/bin/expr 4 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/3.flag" ]; then outbitsValue=`/usr/bin/expr 8 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/4.flag" ]; then outbitsValue=`/usr/bin/expr 16 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/5.flag" ]; then outbitsValue=`/usr/bin/expr 32 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/6.flag" ]; then outbitsValue=`/usr/bin/expr 64 + $outbitsValue`; fi
#if [ -e "$OUTBITSDIR/7.flag" ]; then outbitsValue=`/usr/bin/expr 128 + $outbitsValue`; fi
#echo OUTBITS value $outbitsValue
# k8055 binary.
KBIN=/usr/local/bin/k8055
# Data output file.
DATAOUT=${WORKDIR}/k8055.out
# Previous output file, if any.
DATABAK=${WORKDIR}/k8055.bak
# Log directory for long-term storage.
LOGDIR=/local/k8055/data
# Power-change flag.
PWRCHG=${WORKDIR}/EXT_POWER_CHANGE.flag
# Default LED to off if we're not in a known-good state.
echo "none" > /sys/class/leds/plug\:green\:health/trigger
# If executable is missing, remove data file and quit.
# Set all the warning flags as a precaution.
if [ ! -x $KBIN ]; then
/bin/rm -f $DATAOUT $DATABAK $BATTGOOD
echo UNKNOWN > $BATTWARN
echo UNKNOWN > $BATTVLOW
#echo UNKNOWN > $PWRCHG
/etc/suspend_inactive_DT200.sh powerdown > /tmp/DT200-k8055.log
exit 1
fi
# Now try to ensure our power is on and the device is awake...
# Don't autosuspend immediately (0) as this seems to cause unreliability.
# Note the silly trailing space in the manufacturer name.
DEV=`/etc/findUSBdir.sh -sloppy "Velleman " "USB K8055"`
if [ "x$DEV" != "x" -a -d "$DEV" ]; then
echo 1 > $DEV/power/autosuspend
echo auto > $DEV/power/level
fi
# If we can't collect any data from port 0, remove data file and quit.
# Set all the warning flags as a precaution.
DATA="`$KBIN -port:0 -d:$outbitsValue 2>/dev/null`"
DATANF=`echo "$DATA" | /usr/bin/awk '-F;' '{print NF;}'`
if [ "6" != "$DATANF" ]; then
/bin/rm -f $DATAOUT $DATABAK $BATTGOOD
echo UNKNOWN > $BATTWARN
echo UNKNOWN > $BATTVLOW
#echo UNKNOWN > $PWRCHG
/etc/suspend_inactive_DT200.sh powerdown > /tmp/DT200-k8055.log
exit 1
fi
# Move aside previous data file, if any.
if [ -f $DATAOUT ]; then /bin/mv -f $DATAOUT $DATABAK; fi
# Atomically update captured data.
# Format: (timestamp);(digital);(analog 1);(analog 2);(counter 1);(counter 2)
echo "$DATA" > $DATAOUT.tmp && /bin/mv -f $DATAOUT.tmp $DATAOUT
# Store log files iff output directory exists.
# (And if not explicitly disabled for this run.)
# Only claim resolution to the minute.
if [ "$NOLOG" != "true" -a -d ${LOGDIR} ]; then
# Store archive/dated copy of data.
FINALLOGDIR="${LOGDIR}/`date -u +%Y/%m/%d`"
if [ ! -d ${FINALLOGDIR} ]; then
mkdir -p ${FINALLOGDIR}
else
# Ensure the month dir updates for each point added to it
# to help track dependencies (at the cost of some disc traffic).
touch `dirname ${FINALLOGDIR}`
fi
chmod a+rx ${FINALLOGDIR}
FINALLOGFILE="${FINALLOGDIR}/`date -u +%H%M`.dat"
echo "$DATA" > $FINALLOGFILE
chmod a+r $FINALLOGFILE
# Atomically update 'last' data received AFTER the archive copy
# so Make (etc) can watch this file's timestamp.
echo "$DATA" > ${LOGDIR}/last.dat.tmp
mv ${LOGDIR}/last.dat.tmp ${LOGDIR}/last.dat
fi
## Update 'change' before main status to avoid races when ramping up performance.
## If the digital input counters for bits 0 and 1 have changed since last time
## indicating transitions between different battery SOC (states of charge)
## (or that there was no previous data sample)
## then flag the power status as (significantly) changed.
## The fields checked are 5 and 6, eg "3339" and "201" in
## the sample "271;0;134;133;3339;201".
#if [ ! -f $DATABAK ]; then
# # No previous sample available, so flag as a change.
# echo MISSING > $PWRCHG
#elif [ "`awk < $DATAOUT '-F;' '{print $5, $6}'`" != "`awk < $DATABAK '-F;' '{print $5, $6}'`" ]; then
# # Flag a change.
# echo CHANGE > $PWRCHG
#else
# # No significant change from previous value.
# /bin/rm -f $PWRCHG
#fi
# Meaning of various inputs as of 20090922:
# Digital input (bit 0 is least significant):
# 0: Not assigned.
# 1: Not assigned.
# 2: Not assigned.
# 3: True/1 if output from the wind turbine is available.
# 4: Not yet assigned, so should always be false/0.
# Analogue input:
# 1: Approx battery/supply volts in 0.1V, eg 125 indicates ~12.5V.
# 2: Not assigned.
# Meaning of various inputs as of 20070904:
# Digital input (bit 0 is least significant):
# 0: True/1 if battery voltage indicates good charge and currently charging.
# 1: Not assigned.
# 2: True/1 if input to DC-DC converter is good (no low-voltage disconnect).
# 3: True/1 if output from the wind turbine is available.
# 4: Not yet assigned, so should always be false/0.
# Primary/SLA (gel) battery voltage thresholds in units of 0.1V.
# At/above 'BATTVH' (very high) means in absorption (a little below 14.0V).
# Above 'High' means mainly full and charging/floating. ~13.45V at 80% SoC.
# DHD20120725: set at 13.2V to allow for summer temperature compensation.
# Above 'Low' means probably reasonably full and/or charging but no 'excess'.
# At or below 'BATTVL' (very low) means urgent conservation needed.
# See LA SoC chart: http://solarjohn.blogspot.com/2007/03/measuring-battery-state-of-charge.html
BATTVH=138
BATTH=132
# A BATTL of 12.6V to 12.8V is 100%-full resting/light-discharge threshold.
BATTL=126
# 12.1V agrees with amber light on MS MPPT ctrl but only leaves ~1 day @ 40Ah.
# 12.2V suggests about 50% SoC minus slight drain by plug.
# 12.4V suggests maybe 60% SoC minus slight drain by plug.
BATTVL=123
# Secondary/LiFePO4 battery voltage thresholds in units of 0.1V.
# At or below 'BATTL2' the battery is not full nor nearly full.
# At or below 'BATTVL2' the battery is nearly empty.
BATTL2=131
BATTVL2=127
# Unless generation forecast is good then adjust thresholds to conserve further.
if [ ! -e $FORECASTGENGOOD ]; then
BATTH=133
BATTL=128
BATTVL=126
BATTL2=132
BATTVL2=130
fi
# Extract the primary/SLA battery voltage.
BATTV=`echo "$DATA" | /usr/bin/awk '-F;' '{ print $3; }'`
# Extract the secondary/LiFePO4 battery voltage.
BATTV2=`echo "$DATA" | /usr/bin/awk '-F;' '{ print $4; }'`
# Put the battery voltage (in 0.1v) where it can be seen...
echo $BATTV > /var/run/BATTV.new
chmod 644 /var/run/BATTV.new
mv /var/run/BATTV.new /var/run/BATTV
chmod 644 /var/run/BATTV
# This is written to minimise the code to be executed when power is low.
if [ "$BATTV" -le "$BATTVL" -o "$BATTV2" -le "$BATTVL2" ]; then
# Either battery very low: urgent conservation required.
# Ensure set LOW and VLOW flags.
# Always at least touch $BATTVLOW to enable minimum poll interval mechanism.
if [ ! -e $BATTVLOW ]; then echo VLOW > $BATTVLOW; else touch $BATTVLOW; fi
if [ ! -e $BATTWARN ]; then echo LOW > $BATTWARN; fi
# Unconditionally remove the GOOD flag.
/bin/rm -f $BATTGOOD
# As a precaution, in case power will soon fail,
# flush disc data to permanent storage now.
# Flash the LED on while we're doing it...
echo "default-on" > /sys/class/leds/plug\:green\:health/trigger
sync
echo "none" > /sys/class/leds/plug\:green\:health/trigger
elif [ "$BATTV" -le "$BATTL" -o "$BATTV2" -le "$BATTL2" ]; then
# No 'excess' (but SoC OK), eg batteries not full and/or sun not out.
# Unconditionally remove the GOOD and VLOW flags.
/bin/rm -f $BATTGOOD $BATTVLOW
# Set LOW flag if absent.
if [ ! -e $BATTWARN ]; then echo LOW > $BATTWARN; fi
# Turn the light off.
echo "none" > /sys/class/leds/plug\:green\:health/trigger
elif [ "$BATTV" -gt "$BATTH" ]; then
# Excess: batteries full and plenty more energy available.
# Unconditionally remove LOW and VLOW flags.
/bin/rm -f $BATTWARN $BATTVLOW
# Set 'good' flag if above float...
# Only touch if not present to allow measuring its age...
if [ "$BATTV" -ge "$BATTVH" -a ! -e $BATTGOOD ]; then touch $BATTGOOD; fi
# Show a full-on light to show that all is well (but burn some juice).
echo "default-on" > /sys/class/leds/plug\:green\:health/trigger
else
# In between low and high thresholds.
# Unconditionally VLOW flag.
/bin/rm -f $BATTVLOW
# For hysteresis,
# leave the LOW flag set or unset as per last excursion high or low.
# If 'high' then show a heartbeat light to indicate temporary sag...
# If 'low' then turn the light off.
#if [ ! -e $BATTWARN ]; then
# echo "heartbeat" > /sys/class/leds/plug\:green\:health/trigger
#else
echo "none" > /sys/class/leds/plug\:green\:health/trigger
#fi
fi
# Now try to reduce power consumed by storage/USB
# (because polling k8055 seems to wake it up)
# unless we've really got lots of excess juice in the system to dump.
if [ "$BATTV" -le "$BATTH" ]; then
/etc/suspend_inactive_DT200.sh powerdown > /tmp/DT200-k8055.log
fi
# When we've finished, try to ensure that our consumption is as low as possible.
if [ "x$DEV" != "x" -a -d "$DEV" ]; then
echo 1 > $DEV/power/autosuspend
echo auto > $DEV/power/level
fi
exit 0