Skip to content

Lambda: support for excess energy specified in negative numbers #21972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 8, 2025

Conversation

anbie
Copy link
Contributor

@anbie anbie commented Jun 21, 2025

#20238

This PR introduces two enhancements to the lambda-zewotherm template:
1. Inverted Power Write for Negative Excess Mode
Adds support for inverting the power value written via Modbus when operating in "negative excess" mode.
2. Customizable Modbus TCP Port
Introduces a new port parameter, allowing users to override the default Modbus TCP port 502.

As noted in #20238, there is still an outstanding issue when using the "negative" mode: negative values are being overwritten with 0 during processing. Further investigation is needed.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @anbie - I've reviewed your changes - here's some feedback:

  • Consolidate the repeated Modbus URI blocks into a shared variable or template to reduce duplication.
  • Add a guard or diagnostic in the negative excess mode to catch the known zero-overwrite issue when scaling power writes.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consolidate the repeated Modbus URI blocks into a shared variable or template to reduce duplication.
- Add a guard or diagnostic in the `negative` excess mode to catch the known zero-overwrite issue when scaling power writes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@andig
Copy link
Member

andig commented Jun 23, 2025

Wenn ich in die plugin/modbus.go ein Print mit einbaue sieht man, dass da schon nur noch -0 ankommt:

	return func(val float64) error {
		val *= m.scale

		fmt.Println(val)

		switch op.FuncCode {
		case gridx.FuncCodeWriteSingleCoil:
			var uval uint16
			if val != 0 {
				uval = 0xFF00
			}
			_, err = m.conn.WriteSingleCoil(op.Addr, uval)
			return err

Vmtl. passiert der Fehler vorher im Template.

@andig andig added the heating Heating label Jun 23, 2025
@anbie
Copy link
Contributor Author

anbie commented Jun 23, 2025

Gute Frage – ich bin leider auch noch nicht weitergekommen, was genau mit dem Wert passiert.
Testweise habe ich die „-1“ im „negative“-Fall mal durch eine „2“ ersetzt. Damit konnte ich sehen, dass ein verdoppelter Wert durchgereicht wird – der Pfad scheint also grundsätzlich zu funktionieren.
Bessere Debug-Möglichkeiten (z. B. Traces o. Ä.) habe ich bisher leider nicht gefunden.
Ich bin aber noch am Lernen und weiter am Forschen.

@anbie
Copy link
Contributor Author

anbie commented Jun 23, 2025

Da ich in Go nicht ganz zu Hause bin, habe ich etwas mit Trace-Statements in der writeFunc() experimentiert, um das Verhalten besser zu verstehen.

Was mir dabei aufgefallen ist:

  • Beim Anlegen bzw. Validieren der Lambda wird initial der Wert 0 geschrieben.
  • Erst wenn der Charger bereits existiert und tatsächlich „geladen“ wird, erscheinen reale Leistungswerte (Watt). Ich lade hier mit "fast" und sehe also grob 11kW.
  • Die modbus "payload" ist dann weiterhin 0x0000
	return func(val float64) error {

		fmt.Println("▶ writeFunc called")
		fmt.Println(" raw val:", val)


		fmt.Println(" m.scale:", m.scale)


		val *= m.scale
		fmt.Println(" scaled val:", val)
▶ writeFunc called
 raw val: 11040
 m.scale: -1
 scaled val: -11040
[db:1  ] TRACE 2025/06/23 23:46:33 modbus: send 00 31 00 00 00 09 01 10 00 66 00 01 02 00 00
[db:1  ] TRACE 2025/06/23 23:46:33 modbus: recv 00 31 00 00 00 06 01 10 00 66 00 01
[site  ] DEBUG 2025/06/23 23:46:49 ----

@anbie
Copy link
Contributor Author

anbie commented Jun 24, 2025

Es sieht so aus, als würde encode Probleme haben mit negativen Werten.
(bin erstmal busy für die nächsten Stunden - deswegen hier nur ein kurzer Zwischenbericht)

Trace Output

▶ writeFunc called
 raw val: 11040
 m.scale: -1
 scaled val: -11040
▶ func called
 v: 0
 b: [0 0]
Call to WriteMultipleRegisters
 b: [0 0]
 length 1
[db:1  ] TRACE 2025/06/24 08:46:02 modbus: send 00 0e 00 00 00 09 01 10 00 66 00 01 02 00 00
[db:1  ] TRACE 2025/06/24 08:46:02 modbus: recv 00 0e 00 00 00 06 01 10 00 66 00 01

modbus.go:

		fmt.Println("▶ writeFunc called")
		fmt.Println(" raw val:", val)

		fmt.Println(" m.scale:", m.scale)

		val *= m.scale
		fmt.Println(" scaled val:", val)
		[...]
		case gridx.FuncCodeWriteMultipleRegisters:
			b, err := encode(val)
			if err == nil {
				fmt.Println("Call to WriteMultipleRegisters")
				fmt.Println(" b:", b)
				fmt.Println(" length", op.Length)
				_, err = m.conn.WriteMultipleRegisters(op.Addr, op.Length, b)
			}
			return err

register.go:

	return func(f float64) ([]byte, error) {
		fmt.Println("▶ func called")
		v := fun(f)
		b := make([]byte, 2*length)
		fmt.Println(" v:", v)
		fmt.Println(" b:", b)

Wenn ich val = 5 mache, dann sieht das so aus:

▶ func called
 v: 5
 b: [0 0]
Call to WriteMultipleRegisters
 b: [0 5]
 length 1
[db:1  ] TRACE 2025/06/24 08:43:06 modbus: send 00 2c 00 00 00 09 01 10 00 66 00 01 02 00 05
[db:1  ] TRACE 2025/06/24 08:43:06 modbus: recv 00 2c 00 00 00 06 01 10 00 66 00 01

@andig
Copy link
Member

andig commented Jun 24, 2025

Es liegt also an der übergebenen encode Funktion. Bzw. an der inneren fun()

@anbie
Copy link
Contributor Author

anbie commented Jun 25, 2025

Ich habe in dem Zusammenhang einiges über Go dazugelernt – das Problem versteckt sich in util/modbus/register.go in der EncodeFunc():

func (r Register) EncodeFunc() (func(float64) ([]byte, error), error) {
	enc := strings.ToLower(r.encoding())

	switch {
	case strings.HasPrefix(enc, "bool"):
		fallthrough

	case strings.HasPrefix(enc, "int") || strings.HasPrefix(enc, "uint"):
		return r.encodeToBytes(func(v float64) uint64 {
			return uint64(v)
		})

Ich habe das mal ersetzt mit diesem Code, um auch negative Werte korrekt in die Lambda zu bekommen:

	case strings.HasPrefix(enc, "int16"):
		// get register length up front
		length, err := r.Length()
		if err != nil {
			return nil, err
		}

		return r.encodeToBytes(func(v float64) uint64 {
			// Round to nearest integer
			i := int64(math.Round(v))

			// Compute bit-width for int16
			bits := 16 * int(length)
			min := -(1 << (bits - 1))
			max := (1 << (bits - 1)) - 1

			// Two’s-complement mask for signed
			mask := uint64((1 << bits) - 1)
			return uint64(uint64(i) & mask)
		})

	case strings.HasPrefix(enc, "uint16"):
        ...

Vollständige Transparenz: Ich bin in Go nicht wirklich tief drin und habe mir bei der Umsetzung Unterstützung von ChatGTP geholt. Daher kann ich schwer einschätzen, ob der Ansatz hier „gut“ und elegant gelöst ist – Feedback, Anmerkungen oder Verbesserungsvorschläge sind sehr willkommen! 🙏

@andig
Copy link
Member

andig commented Jun 25, 2025

Vorschlag für die EncodeFunc:

	case strings.HasPrefix(enc, "int"):
		return r.encodeToBytes(func(v float64) uint64 {
			return uint64(int64(v))
		})

	case strings.HasPrefix(enc, "uint"):
		return r.encodeToBytes(func(v float64) uint64 {
			return uint64(v)
		})

ob/wie das downscaling des Vorzeichens funktioniert bräuchte einen Unit Test, aber erstmal reichts das auszuprobieren ;)

@anbie
Copy link
Contributor Author

anbie commented Jun 25, 2025

Hab Deinen Vorschlag eingebaut und kurzen Unit-Test gemacht: das sieht vielverspechend aus.

▶ func called
 f -11040
 fun() points to: github.com/evcc-io/evcc/util/modbus.Register.EncodeFunc.func1
 b: [0 0]
 Call to WriteMultipleRegisters
 b: [212 224]
[db:1  ] TRACE 2025/06/25 20:36:40 modbus: send 00 1a 00 00 00 09 01 10 00 66 00 01 02 d4 e0
[db:1  ] TRACE 2025/06/25 20:36:40 modbus: recv 00 1a 00 00 00 06 01 10 00 66 00 01

Die Data Payload, die zur Lambda Register 102 geschickt wird, ist 0xD4E0 -> "-11008".

@andig andig changed the title feat: Add support for Lambda excess energy specified in negative numbers Lambda: support for excess energy specified in negative numbers Jul 8, 2025
@andig andig merged commit 72b64d0 into evcc-io:master Jul 8, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
heating Heating
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy