Added a primitive battery charge monitor.

This commit is contained in:
Hendrik van Wyk
2015-02-08 11:28:04 +02:00
parent e26aa12dce
commit 3cde999de9
4 changed files with 62 additions and 6 deletions

View File

@@ -44,7 +44,7 @@ func main() {
flag.Parse() flag.Parse()
source := datasource.NewJSONSource(*url) source := datasource.NewJSONSource(*url)
gui := webgui.NewWebGui(source, 10*time.Second) gui := webgui.NewWebGui(source, 10*time.Second, 100)
http.Handle("/", gui) http.Handle("/", gui)
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
} }

35
webgui/charge_tracker.go Normal file
View File

@@ -0,0 +1,35 @@
package webgui
import (
"time"
)
type ChargeTracker struct {
fullLevel float64
currentLevel float64
lastUpdate time.Time
}
// Creates a new charge tracker. fullLevel is in A h.
func NewChargeTracker(fullLevel float64) *ChargeTracker {
return &ChargeTracker{
fullLevel: fullLevel,
currentLevel: fullLevel, // Have to start somewhere.
lastUpdate: time.Now(),
}
}
func (c *ChargeTracker) Update(amp float64) {
newNow := time.Now()
elapsed := newNow.Sub(c.lastUpdate).Hours()
c.lastUpdate = newNow
c.currentLevel -= elapsed * amp
}
func (c *ChargeTracker) CurrentLevel() float64 {
return c.currentLevel
}
func (c *ChargeTracker) Reset() {
c.currentLevel = c.fullLevel
}

View File

@@ -65,6 +65,7 @@ var htmlTemplate string = `<html>
<dt> Battery Current: {{.BatCurrent}} A</dt> <dt> Battery Current: {{.BatCurrent}} A</dt>
<dt> Battery Voltage: {{.BatVoltage}} V</dt> <dt> Battery Voltage: {{.BatVoltage}} V</dt>
<dt> Battery Power: {{.BatPower}} W</dt> <dt> Battery Power: {{.BatPower}} W</dt>
<dt> Battery Charge: {{.BatCharge}} A h</dt>
</dl> </dl>
</body> </body>
</html>` </html>`

View File

@@ -38,6 +38,17 @@ import (
"time" "time"
) )
const (
Temperature = iota
Low_battery
Overload
Inverter
Float
Bulk
Absorption
Mains
)
var leds = map[int]string{ var leds = map[int]string{
0: "Temperature", 0: "Temperature",
1: "Low battery", 1: "Low battery",
@@ -57,7 +68,7 @@ type WebGui struct {
template *template.Template template *template.Template
} }
func NewWebGui(source datasource.DataSource, pollRate time.Duration) *WebGui { func NewWebGui(source datasource.DataSource, pollRate time.Duration, batteryCapacity float64) *WebGui {
wg := new(WebGui) wg := new(WebGui)
wg.source = source wg.source = source
wg.reqChan = make(chan *statusError) wg.reqChan = make(chan *statusError)
@@ -68,7 +79,7 @@ func NewWebGui(source datasource.DataSource, pollRate time.Duration) *WebGui {
if err != nil { if err != nil {
panic(err) panic(err)
} }
go wg.dataPoll(pollRate) go wg.dataPoll(pollRate, batteryCapacity)
return wg return wg
} }
@@ -89,6 +100,7 @@ type TemplateInput struct {
BatVoltage string BatVoltage string
BatCurrent string BatCurrent string
BatPower string BatPower string
BatCharge string
InFreq string InFreq string
@@ -126,6 +138,7 @@ func buildTemplateInput(statusErr *statusError) *TemplateInput {
BatCurrent: fmt.Sprintf("%.3f", status.BatCurrent), BatCurrent: fmt.Sprintf("%.3f", status.BatCurrent),
BatVoltage: fmt.Sprintf("%.3f", status.BatVoltage), BatVoltage: fmt.Sprintf("%.3f", status.BatVoltage),
BatPower: fmt.Sprintf("%.3f", status.BatVoltage*status.BatCurrent), BatPower: fmt.Sprintf("%.3f", status.BatVoltage*status.BatCurrent),
BatCharge: fmt.Sprintf("%.3f", statusErr.chargeLevel),
} }
for i := 7; i >= 0; i-- { for i := 7; i >= 0; i-- {
if status.Leds[i] == 1 { if status.Leds[i] == 1 {
@@ -140,14 +153,16 @@ func (w *WebGui) Stop() {
} }
type statusError struct { type statusError struct {
status datasource.MultiplusStatus status datasource.MultiplusStatus
err error chargeLevel float64
err error
} }
// dataPoll will issue a request for a new status every pollRate. It will send its currently stored status // dataPoll will issue a request for a new status every pollRate. It will send its currently stored status
// to respChan if anything reads from it. // to respChan if anything reads from it.
func (w *WebGui) dataPoll(pollRate time.Duration) { func (w *WebGui) dataPoll(pollRate time.Duration, batteryCapacity float64) {
ticker := time.NewTicker(pollRate) ticker := time.NewTicker(pollRate)
tracker := NewChargeTracker(batteryCapacity)
var statusErr statusError var statusErr statusError
go w.getStatus() go w.getStatus()
gettingStatus := true gettingStatus := true
@@ -164,6 +179,11 @@ func (w *WebGui) dataPoll(pollRate time.Duration) {
} else { } else {
statusErr.status = s.status statusErr.status = s.status
statusErr.err = nil statusErr.err = nil
tracker.Update(s.status.BatCurrent)
if s.status.Leds[Float] == 1 {
tracker.Reset()
}
statusErr.chargeLevel = tracker.CurrentLevel()
} }
gettingStatus = false gettingStatus = false
case w.respChan <- statusErr: case w.respChan <- statusErr: