gemu2015 / Sonoff-Tasmota

Tasmota Fork TCS34725,PN532_i2,ccc1101 Moritz support,m5stack 4,7 epaper, hotplug drivers
GNU General Public License v3.0
24 stars 19 forks source link

MODBUS slave capability? #54

Closed pkkrusty closed 8 months ago

pkkrusty commented 9 months ago

Have you looked for this feature in other issues and in the docs?

Is your feature request related to a problem? Please describe.
Possibility of tasmota device acting as MODBUS slave. If it can be assigned a slave ID and and the function return data from an array, could even happen without user input.

Describe the solution you'd like
User enters slave ID (or even slave ID as array[1], then all registers in the array depending on size defined.

Describe alternatives you've considered
Would allow for long distance two-wire communication between tasmota devices on a single bus.

Additional context
Add any other context or screenshots about the feature request here.

(Please, remember to close the issue when the problem has been addressed)

gemu2015 commented 9 months ago

this is already implemented. i once wanted to adapt my GROWATT inverter to the german 70 percent rule and startet with this MODBUS slave. However this rule is no longer needed, so i did not further test it. here is my initial script for the slave mode. that did work but will certainly need some tweaking

remark: in this example the slave gets its input values from global variables.

needs #define USE_SCRIPT_SERIAL

>D 64
IP=192.168.188.99

; simulate eastron sdm modbus meter
res=0
m:req=0 8
m:resp=0 128

addr=1
regs=0
; maximum allowed export with 10,35 kw modules
limit=7250

; aktueller verbrauch bzw einspeisung
g:zwzc=0
g:zwzi=0
g:zwzo=0

; udp timeout timer
t:udpt=30

col="green"

>BS

res=0
res=so(18 19 9600 8N1 1024)
print open %res%

->setsensor127 0

>G
; reset udp timer
udpt=60

>F
res=sa()
if res>0
then
    ;print res=%res%

    res=sra(req)
    print request = %req[1]% - %req[2]% - %req[3]% - %req[4]% - %req[5]% - %req[6]%

    delay(10)
    ; slave adress
    ; function code
    ; start adress Hi
    ; start adress Lo
    ; np Hi
    ; np Lo
    ; crc Lo
    ; crc Hi

    regs=req[6]/2

    resp[1]=0
    resp[2]=0
    resp[3]=0
    resp[4]=0
    resp[5]=0
    resp[6]=0
    resp[7]=0
    resp[8]=0
    resp[9]=0
    resp[10]=0

    if res==8
    and req[1]==addr
    and req[2]==4
    and req[3]==0
    and req[4]==0 {

    print Reg 0 7
    ; report 230 Volts, current, watts, va
    resp[1]=230
    resp[2]=0
    resp[3]=0
    resp[4]=2
    resp[5]=0
    resp[6]=0
    resp[7]=abs(zwzc)
    smw(addr 3 resp regs)
    }

    if res==8
    and req[1]==addr
    and req[2]==4
    and req[3]==0
    and req[4]==70 {

    print Reg 0x46 5
    ;frequency 0x46, total import, total export, total imp kva, total export kva
    resp[1]=50
    resp[2]=zwzo
    resp[3]=zwzi
    resp[4]=zwzo
    resp[5]=zwzi
    smw(addr 3 resp regs)
    }

    if res==8
    and req[1]==addr
    and req[2]==4
    and req[3]==1
    and req[4]==86 {

    print Reg 0x156 2
    ; report total kwh, kvarh
    resp[1]=zwzo
    resp[2]=zwzo
    smw(addr 3 resp regs)
    }

    if res==8
    and req[1]==addr
    and req[2]==4
    and req[3]==0
    and req[4]==18 {

    print Reg 0x12 7

    ; p1 voltamps  0x12, voltamps reactive
    resp[1]=abs(zwzc)
    resp[2]=0
    resp[3]=0
    resp[4]=abs(zwzc)
    resp[5]= 0
    resp[6]= 0
    resp[7]=1
    smw(addr 3 resp regs)
    }

endif

>S

; upd timeout
if udpt==0 {
    gvr
    udpt=-1
}

if zwzc<0
then
col="green"
else
col="red"
endif

>W
ZRZ total Verbrauch: {m}<span style="color:red;">%0zwzi%  W</span>
ZRZ total Einspeisung: {m}<span style="color:green;">%0zwzo%  W</span>
ZRZ akt Verbrauch/Einspeisung: {m}<span style="color:%col%;">%0zwzc%  W</span>
pkkrusty commented 9 months ago

In documentation smw() has three arguments, address, mode, number, i.e. smw(0 0 65535). But you use in this example four arguments. smw(addr 3 resp regs) I assume undocumented feature that sends array length regs (in this case array of float values)?

Also, usual modbus master address is 0, so if I am communicating with another tasmota device that is using SML scripting, it will be master with address 0, correct?

gemu2015 commented 9 months ago
  1. smw(ADDR MODE ARRAY NVALS ) NVALS = number of values of array to send

  2. yes

gemu2015 commented 9 months ago

another user wanted a modbus slave with TCP. (SML reader simulates a TCP modbus device)

so i added TCP IO support today.

in case you are also interested i will post the new commands here

Pewibe commented 9 months ago

In these days I got a new central heating Type Solvis Ben which provides all Sensor and counting values by TCP Modbus. So I might be one future user of your implementation. Thanks in advance, but I guess it takes some weeks for me to do a first test. Maybe you can collect your postings into one document related to Modbus or whatever to have one common source.

gemu2015 commented 9 months ago

syntax of TCP interface

define USE_SCRIPT_TCP_SERVER

wso(port) start a tcp stream server at port

wsc() close tcp stream server

wsa() return bytes available on tcp stream

str=wsrs() return a string read from tcp stream

wsws(string) writes a string to tcp stream

wsra(array) reads a tcp stream into array

wswa(array num) writes num bytes of array to tcp stream

Pewibe commented 9 months ago

Thanks Gerhard. Any additional hint for me to understand the modbus stream / protocol? I was wondering that the wso() do not need an IP address. Is this because a slave is waiting until the server likes to talk to him? In this case I guess my heating controller is a modbus slave itself and an information reading device has to act as server. By the way, have you heard anything about Solvis Control 3 (SC-3) or a guy who wants to do the same like me?

gemu2015 commented 9 months ago

we are talking from a modbus slave that simulates a modbus tcp meter

a TCP modbus master is implemented in the SML driver. (see docs)

sorry i have no knowledge about any Solvis user.

gemu2015 commented 9 months ago

this emulates an SDM630 meter

>D
IP=192.168.188.106

; emulate SDM630 (has float registers)

res=0
cnt=0
xcnt=0
once=0
reg=0
ind=0
nval=0

M:req=0 16
M:resp=0 64

>BS
; start server
res=wso(502)
print res=%res%

; set register values
#dosel
switch reg
    case 0
        resp[ind]=230   
    case 2
        resp[ind]=231
    case 4
        resp[ind]=232
    case 6
        resp[ind]=10    
    case 8
        resp[ind]=20
    case 10
        resp[ind]=30
    case 12
        resp[ind]=100   
    case 14
        resp[ind]=200
    case 16
        resp[ind]=300
ends

>F
; check incomming data
xcnt =wsa()
if xcnt>0 {
    print bytes=%xcnt%
    ; read request
    res=wsra(req)

    reg=req[10]
    print register %reg%

    nval=req[12]
    print count %nval%

    ; transaction id
    resp[1]=req[1]
    resp[2]=req[2]
    ; protocol id
    resp[3]=req[3]
    resp[4]=req[4]
    ; length
    resp[5]=0
    resp[6]=req[12]*2+3
    ;device address
    resp[7]=req[7]
    ; function code
    resp[8]=req[8]
    ; payload len
    resp[9]=req[12]*2

    ; write response
    wswa(resp 9)

    resp[1]=99

    ; 3 phase voltage, current, power values
    ind=1
    for cnt 1 nval 2
        =#dosel
        reg+=2
        ind+=1
    next
    ; write data part (float values)
    wswa(resp ind-1 3)
}

>R
; close server
res=wsc()

this is a master modbus script for sdm630

>D
IP=192.168.188.117
>B  
->sensor53 r

; multiple float register request
>M 1  
+1,[192.168.188.106],m,0,502,SDM630,0,10,r010400000006,r010400060006,r0104000c0006
1,01040cffffffff@i0:1,Voltage P1,V,Voltage_P1,2  
1,01040cx4ffffffff@i0:1,Voltage P2,V,Voltage_P2,2  
1,01040cx8ffffffff@i0:1,Voltage P3,V,Voltage_P3,2  
1,01040cffffffff@i1:1,Current P1,A,Current_P1,2  
1,01040cx4ffffffff@i1:1,Current P2,A,Current_P2,2  
1,01040cx8ffffffff@i1:1,Current P3,A,Current_P3,2  
1,01040cffffffff@i2:1,Active Power P1,W,Power_P1,2  
1,01040cx4ffffffff@i2:1,Active Power P2,W,Power_P2,2  
1,01040cx8ffffffff@i2:1,Active Power P3,W,Power_P3,2  
#  

; single float register request
>xM 1  
+1,[192.168.188.106],m,0,502,SDM630,0,10,01040000,01040002,01040004,01040006,01040008,0104000a,0104000c,0104000e,01040010  
1,010404ffffffff@i0:1,Voltage P1,V,Voltage_P1,2  
1,010404ffffffff@i1:1,Voltage P2,V,Voltage_P2,2  
1,010404ffffffff@i2:1,Voltage P3,V,Voltage_P3,2  
1,010404ffffffff@i3:1,Current P1,A,Current_P1,2  
1,010404ffffffff@i4:1,Current P2,A,Current_P2,2  
1,010404ffffffff@i5:1,Current P3,A,Current_P3,2  
1,010404ffffffff@i6:1,Active Power P1,W,Power_P1,2  
1,010404ffffffff@i7:1,Active Power P2,W,Power_P2,2  
1,010404ffffffff@i8:1,Active Power P3,W,Power_P3,2  
#