Skip to content

Commit

Permalink
Refactor to support Wallbe dip 10 enable/disable
Browse files Browse the repository at this point in the history
  • Loading branch information
andig committed Mar 18, 2020
1 parent 3c503b3 commit da74ce1
Show file tree
Hide file tree
Showing 48 changed files with 1,243 additions and 1,362 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ audi.*
credentials.json
token.json
assets/js/debug.js
evcc
__debug_bin
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ issues:
exclude:
- "func `..Wallbe..showIO` is unused"
- "func `..Wallbe..showIOs` is unused"
- .regOverchargeProtect. is unused
- .regActualCurrent. is unused
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ FROM alpine
# Import from builder.
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd

# Copy our static executable
COPY --from=builder /build/evcc /usr/local/bin/evcc
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ build:

publish-images:
@echo Version: $(VERSION) $(BUILD_DATE)
seihon publish -v "$(TAG_NAME)" -v "latest" --image-name andig/evcc-base --base-runtime-image alpine --dry-run=false --targets=arm.v6,amd64
# seihon publish -v "$(TAG_NAME)" -v "latest" --image-name andig/evcc1 --base-runtime-image alpine --dry-run=false --targets=arm.v6,amd64
docker build -t "andig/evcc:latest" . && docker push andig/evcc:latest

test-release:
goreleaser --snapshot --skip-publish --rm-dist
Expand Down
90 changes: 51 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

EVCC is an EV Charge Controller implemented in [Go](2). It comes with a bundled implementation for Wallbe chargers but supports any type of charger or meter through scripting and integration with MQTT.

Features:

- support for Wallbe chargers
- support for any charger, meter or EV using scripting
- integration with home automation - supports shell scripts and MQTT
- soft ramp-up/ramp-down of charge current
- electric contactor protection
- clean, non-bloat user interface

![Screenshot](docs/screenshot.png)

**NOTE:** You are using this software **entirely** at your own risk. It is your responsibility to verify it is working as intended.

## Background
Expand All @@ -15,21 +26,27 @@ Hence, for a simplified and stricter implementation of an EV charge controller,
- typed language with ability for systematic testing - achieved by using [Go](2)
- structured cnofiguration - supports YAML-based [config file](evcc.dist.yaml)
- avoidance of feature bloat, simple and clean UI - utilizes [Bootstrap](3)
- integration with home automation - supports shell scripts and MQTT
- containerized operation beyond Raspbery Pi - provide multi-arch [Docker Image](4)
- support for multiple load points - tbd

## User interface
## Installation

EVCC features a clean, non-bloat user interface:
### Hardware

![Screenshot](docs/screenshot.png)
EVCC requires a supported charger and a combination of grid, PV and charge meter.
Charger and meters MUST be professionally installed.

## Installation
#### Wallbe Charger

To build EVCC from source, [Go](2) 1.13 is required:
Wallbe chargers are supported out of the box. The Wallbe must be connected using Ethernet. If not configured, the default address `192.168.0.8:502` is used.

make
To allow controlling charge start/stop, the Wallbe physical configuration must be modified. This requires opening the Wallbe and should only be done by professionals. Once opened, DIP 10 must be set to ON:

![dip10](docs/dip10.jpeg)

More information on interacting with Wallbe chargers can be found at [GoingElectric](https://www.goingelectric.de/forum/viewtopic.php?p=1212583). Use with care.

### Software

The preferred way of running EVCC is using the docker image:

Expand All @@ -43,56 +60,52 @@ To run EVCC with given config file and UI on port 7070:

docker run -v $(pwd)/evcc.dist.yaml:/etc/evcc.yaml -p 7070:7070 andig/evcc-bundle

To build EVCC from source, [Go](2) 1.13 is required:

make

## Configuration

### Charge Modes

Three operation modes are supported:
Multiple charge modes are supported:

- **Off**: disable the charger, even if car gets connected.
- **Now** (**Sofortladen**): charge immediately with maximum allowed current.
- **Min + PV**: charge immediately with minimum configured current. Additionally use PV if available.
- **PV**: use PV as available. May not charge the car if PV remains dark.

In general, due to the minimum value of 5% for signalling the EV duty cycle, the charger cannot limit the current to below 6A. If the available power calculation demands a limit less than 6A, handling depends on the charge mode. In **PV** mode,
In general, due to the minimum value of 5% for signalling the EV duty cycle, the charger cannot limit the current to below 6A. If the available power calculation demands a limit less than 6A, handling depends on the charge mode. In **PV** mode, the charger will be disabled until available PV power supports charging with at least 6A. In **Min + PV** mode, charging will continue at minimum current of 6A and charge current will be raised as PV power becomes available again.

### PV generator configuration

For both PV modes, EVCC needs to assess how much residual PV power is available at the grid connection point and how much power the charger actually uses. Various methods are implemented to obtain this information, with different degrees of accuracy.

#### PV meter

Configuring a *PV meter* is the simplest option. *PV meter* measures the PV generation. The charger is allowed to consume:

Charge Power = PV Meter Power - Residual Power
- **PV meter**: Configuring a *PV meter* is the simplest option. *PV meter* measures the PV generation. The charger is allowed to consume:

The *Residual Power* is a configurable assumption how much power remaining facilities beside the charger use.
Charge Power = PV Meter Power - Residual Power

#### Grid meter
The *Residual Power* is a configurable assumption how much power remaining facilities beside the charger use.

Configuring a *grid meter* is the preferred option. The *grid meter* is expected to be a two-way meter (import+export) and return the current amount of grid export as negative value. The charger is then allowed to consume:
- **Grid meter**: Configuring a *grid meter* is the preferred option. The *grid meter* is expected to be a two-way meter (import+export) and return the current amount of grid export as negative value measured in kWh. The charger is then allowed to consume:

Δ Charge Power = Grid Meter Power - Residual Power
Δ Charge Power = Grid Meter Power - Residual Power

rounded down and capped at zero and
rounded down and capped at zero and

Charge Power = Current Charge Power + Δ Charge Power
Charge Power = Current Charge Power + Δ Charge Power

In this setup, *residual power* is used margin to account for fluctuations in PV production that may be faster than EVCC's control loop.
In this setup, *residual power* is used as margin to account for fluctuations in PV production that may be faster than EVCC's control loop.

### Charger configuration

When using a *grid meter* for accurate control of PV utilization, EVCC needs to be able to determine the current charge power. There are two configurations for determining the *current charge power*:

#### Charge meter

A *charge meter* is often integrated into the charger but can also be installed separately. EVCC expects the *charge meter* to supply *charge power* and preferably also *total energy*.
- **Charge meter**: A *charge meter* is often integrated into the charger but can also be installed separately. EVCC expects the *charge meter* to supply *charge power* and preferably also *total energy*.
If *total energy* is supplied, it can be used to calculate the *charged energy* for the current charging cycle.

#### No charge meter

If not charge meter is installed, *charge power* is deducted from *charge current* as controlled by the charger. This method is less accurate than using a *charge meter* since the EV may chose to use less power than EVCC has allowed for consumption.
If the charger supplies *total energy* for the charging cycle this value is preferred over the *charge meter*'s value (if present)
- **No charge meter**: If no charge meter is installed, *charge power* is deducted from *charge current* as controlled by the charger. This method is less accurate than using a *charge meter* since the EV may chose to use less power than EVCC has allowed for consumption.
If the charger supplies *total energy* for the charging cycle this value is preferred over the *charge meter*'s value (if present).

## Implementation

Expand All @@ -102,45 +115,44 @@ EVCC consists of four basic elements: *Charger*, *Meter*, *SoC* and *Loadpoint*.

Charger is reponsible for EV state handling:

- `Status()`
- `Enabled()`
- `Enable(enable bool)`
- `ActualCurrent()`
- `MaxCurrent(current int64)` (`ChargeController` only)
- `Status()`: get charge controller status (`A...F`)
- `Enabled()`: get charger availablity
- `Enable()`: set charger availability
- `MaxCurrent()`: set maximum allowed charge current in A

Available charger implementations are:

- `wallbe`: implements the interface to the Wallbe Eco chargers
- `configurable`: default charger implementation using configurable plugins for accessing device data
- `default`: default charger implementation using configurable plugins for integrating any type of charger

### Meter

Meters provide data about power and energy consumption:

- `CurrentPower()`
- `TotalEnergy()` (`MeterEnergy` only)
- `CurrentPower()`: power in W
- `TotalEnergy()`: energy in kWh (optional)

Meter has a single implementaton where meter readings- power and energy- can be configured to be delivered by plugin.

### SoC

SoC represents a specific EV battery. Configuring a SoC allows to define it's `Capacity (kWh)` and dynamically provide:

- `ChargeState()`
- `ChargeState()`: state of charge in %

If SoC is configured and assigned to the charger, charge status and remaining charge duration become available in the user interface.

### Loadpoint

Loadpoint controls the Charger behaviour according to the operations mode- off, now, pv + minimum or pv only.
Loadpoint controls the Charger behaviour according to the operations mode- *off*, *now*, *PV + minimum* or *PV only*.

## Plugins

Plugins are used to implement accessing and updating generic data sources. EVCC supports the following *read/write* plugins:

- `mqtt`: this plugin allows to read values from MQTT topics. This is particularly useful for meters, e.g. when meter data is already available on MQTT. See [MBMD](5) for an example how to get Modbus meter data into MQTT.
This plugin type is read-only and does not provide write access.
- `exec`: the exec plugin executes external scripts to read or update data. This plugin is useful to implement any type of external functionality.
- `script`: the script plugin executes external scripts to read or update data. This plugin is useful to implement any type of external functionality.

When using plugins for *write* access, the actual data is provided as variable in form of `${var[:format]}`. The variable is replaced with the actual data before the plugin is executed.

Expand Down
48 changes: 21 additions & 27 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ package api

import "time"

//go:generate mockgen -package mock -destination ../mock/mock.go github.com/andig/evcc/api Charger,ChargeController,Meter,MeterEnergy
//go:generate mockgen -package mock -destination ../mock/mock.go github.com/andig/evcc/api Charger,Meter,MeterEnergy

// Meter is able to provide current power at metering point
type Meter interface {
CurrentPower() (float64, error)
}
// ChargeMode are charge modes modeled after OpenWB
type ChargeMode string

// MeterEnergy is able to provide current power at metering point
type MeterEnergy interface {
TotalEnergy() (float64, error)
}
const (
ModeOff ChargeMode = "off"
ModeNow ChargeMode = "now"
ModeMinPV ChargeMode = "minpv"
ModePV ChargeMode = "pv"
)

// ChargeStatus is the EVSE models charging status from A to F
// ChargeStatus is the EV's charging status from A to F
type ChargeStatus string

const (
Expand All @@ -27,17 +27,21 @@ const (
StatusF ChargeStatus = "F" // Fzg. angeschlossen: ja Laden möglich: nein
)

// Meter is able to provide current power in W
type Meter interface {
CurrentPower() (float64, error)
}

// MeterEnergy is able to provide current energy in kWh
type MeterEnergy interface {
TotalEnergy() (float64, error)
}

// Charger is able to provide current charging status and to enable/disabler charging
type Charger interface {
Status() (ChargeStatus, error)
Enabled() (bool, error)
Enable(enable bool) error
ActualCurrent() (int64, error)
// MaxCurrent(current int64) error
}

// ChargeController provides controlling of the charger's max allowed power
type ChargeController interface {
MaxCurrent(current int64) error
}

Expand All @@ -46,21 +50,11 @@ type ChargeTimer interface {
ChargingTime() (time.Duration, error)
}

// ChargeRater provides charged energy amount
// ChargeRater provides charged energy amount in kWh
type ChargeRater interface {
ChargedEnergy() (float64, error)
}

// ChargeMode are charge modes modeled after OpenWB
type ChargeMode string

const (
ModeOff ChargeMode = "off"
ModeNow ChargeMode = "now"
ModeMinPV ChargeMode = "minpv"
ModePV ChargeMode = "pv"
)

// SoC represents the EV battery's state
type SoC interface {
ChargeState() (float64, error)
Expand Down
32 changes: 15 additions & 17 deletions assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,6 @@
</head>
<body>

<div class="container" id="error">
<div class="toast" data-delay="2000" style="position: absolute; z-index: 2000; top: 1rem; right: 1rem;">
<div class="toast-header">
<strong class="mr-auto">Error</strong>
<small v-if="error.status">HTTP {{error.status}}</small>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">{{error.message}}</div>
</div>
</div>

<div id="app">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="https://github.com/andig/evcc"><i class="text-primary far fa-leaf mr-2"></i>evcc</a>
Expand All @@ -71,6 +58,19 @@
<router-view></router-view>
</div>

<div id="error">
<div class="toast" data-delay="2000" style="position: absolute; top: 4rem; right: 0.5rem;">
<div class="toast-header">
<strong class="mr-auto"><i class="text-danger far fa-exclamation-triangle"></i> Error</strong>
<small v-if="error.status">HTTP {{error.status}}</small>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">{{error.message}}</div>
</div>
</div>

<script type="text/x-template" id="embed-template">
<div class="container mx-auto text-center">
<h1 class="display-4 pt-3" v-if="title">{{title}}</h1>
Expand All @@ -80,7 +80,7 @@ <h1 class="display-4 pt-3" v-if="title">{{title}}</h1>
<iframe v-bind:src="iframe" v-if="iframe">
</a>
<span v-else>
<img v-bind:src="img" v-if="img">
<img class="img-fluid" v-bind:src="img" v-if="img">
<iframe v-bind:src="iframe" v-if="iframe">
</span>
</div>
Expand Down Expand Up @@ -148,11 +148,9 @@ <h4 class="my-0 font-weight-normal">Wallbox</h4>
</div>
<div class="card-body">
<h2 class="card-title pricing-card-title">
{{fmt(state.chargeCurrent)}} <small class="text-muted">A</small>
<span class="text-muted">/</span>
{{fmt(state.chargePower)}} <small class="text-muted">{{fmtUnit(state.chargePower)}}W</small>
</h2>
<p>Strom/Leistung <i class="text-primary far fa-leaf" v-if="state.chargeCurrent"></i></p>
<p>Ladeleistung <i class="text-primary far fa-leaf" v-if="state.chargePower"></i></p>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit da74ce1

Please sign in to comment.