rene Posted November 26, 2020 Share Posted November 26, 2020 Hello, i'm a little stuck. I try to connect with my Solaredge inverter which i enabled modbus over tcp (port 502). It's works with a python script si it's not the Solaredge. The problem is i don't get any out put. The appearance of a few // is because of testing. Also what is confusing that sunspec mentioned about two methods of offset (0 or 40001) which i find confusing. <?php $id = 1; //1 $fc = 3; $offset = 0; //70, ...107 $length = 122; //39, ..3 //Appendix A – Supported MODBUS Request Methods //SolarEdge has implemented two methods of the MODBUS request procedure: //MODBUS request without explicit addressing – supported by communication board CPU version 2.478 and above. For example: //$offset_h = floor($offset/256); //$offset_l = $offset-$offset_h*256; //MODBUS request with explicit register addressing - supported by all communication board CPU versions. For example: $offset_h = floor(($offset+40000)/256); $offset_l = ($offset+40000)-$offset_h*256; $length_h = floor($length/256); $length_l = $length-$length_h*256; //$crc = crc16(chr(0).chr($id).chr(0).chr($fc).chr($offset_h).chr($offset_l).chr($length_h).chr($length_l)); $crc = crc16(chr($id).chr($fc).chr($offset_h).chr($offset_l).chr($length_h).chr($length_l)); $crc_h = floor($crc/256); $crc_l = $crc-$crc_h*256; if ($id<10) {$id0 = "0";} else {$id0 = "";} if ($fc<10) {$fc0 = "0";} else {$fc0 = "";} if ($offset_h<10) {$offset_h0 = "0";} else {$offset_h0 = "";} if ($offset_l<10) {$offset_l0 = "0";} else {$offset_l0 = "";} if ($length_h<10) {$length_h0 = "0";} else {$length_h0 = "";} if ($length_l<10) {$length_l0 = "0";} else {$length_l0 = "";} if ($crc_h<10) {$crc_h0 = "0";} else {$crc_h0 = "";} if ($crc_l<10) {$crc_l0 = "0";} else {$crc_l0 = "";} //$senddata = chr(0).chr($id).chr(0).chr($fc).chr($offset_h).chr($offset_l).chr($length_h).chr($length_l).chr($crc_l).chr($crc_h); $senddata = chr($id).chr($fc).chr($offset_h).chr($offset_l).chr($length_h).chr($length_l).chr($crc_l).chr($crc_h); //without crc ... //$senddata = chr($id).chr($fc).chr($offset_h).chr($offset_l).chr($length_h).chr($length_l); $senddata2 = $id0.dechex($id)." ".$fc0.dechex($fc)." ".$offset_h0.dechex($offset_h).$offset_l0.dechex($offset_l)." ".$length_h0.dechex($length_h).$length_l0.dechex($length_l)." ".$crc_l0.dechex($crc_l).$crc_h0.dechex($crc_h); //$senddata = "11 03 006B 0003 7687"; //$senddata = "1103006B00037687"; //print $senddata."<br />\n"; print $senddata2."<br />\n"; //print dechex($id)." ".dechex($fc)." ".dechex($offset_h).dechex($offset_l)." ".dechex($length_h).dechex($length_l)." "; //print dechex($crc_l).dechex($crc_h); //set_time_limit(0); $fp = fsockopen("192.168.0.192", 502, $errno, $errstr, 3); if(!$fp) { print "can\'t connect modbus tcp device<br />\n"; die(); } else{ print "Connected to modbus<br />\n"; //11 03 0046 0027 xxxx //request 40070 +27 xxxx is crc check //stream_set_timeout($socketHandler, 2); write($fp, $senddata); //write($fp, $senddata,strlen($senddata)); //set_socket_blocking($fp,true); //fputs($fp, $senddata); $response = ''; while (!feof($fp)) { //echo fgets($fp, 128); //stream_set_timeout($socketHandler, 2); //$line[] = fgets($fp, 128); $line[] = fgets($fp); //echo fgets($fp); //echo fread($fp,255); //$response = stream_get_contents($fp); //$line[] = fread($fp); //$response .= fgets($fp, 128); // If you expect an answer } print $response; foreach ($line as $key => $value) { print $key+$offset.":".intval($value)."<br />\n"; } } fclose($fp); function crc16($data) { $crc16 = 0xFFFF; for ($i = 0; $i < strlen($data); $i++) { $crc16 ^=ord($data[$i]); for ($j = 8; $j !=0; $j--) { if (($crc16 & 0x0001) !=0) { $crc16 >>= 1; $crc16 ^= 0xA001; } else $crc16 >>= 1; } } return $crc16; } ?> Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/ Share on other sites More sharing options...
requinix Posted November 26, 2020 Share Posted November 26, 2020 What's the Python script, and is there some documentation online about this sort of thing? Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582659 Share on other sites More sharing options...
rene Posted November 26, 2020 Author Share Posted November 26, 2020 documentation is sunspec for py-script you need pip install pyModbusTCP solaredgemodbus.py #!/usr/bin/env python # -*- coding: utf-8 -*- read_register read 10 registers and print result # on stdout you can use the tiny modbus server "mbserverd" to test this # code mbserverd is here: https://github.com/sourceperl/mbserverd the # command line modbus client mbtget can also be useful mbtget is here: # https://github.com/sourceperl/mbtget from pyModbusTCP.client import ModbusClient import time from datetime import datetime #import http.server #import socketserver #from flask import Flask #app = Flask(__name__) SERVER_HOST = "192.168.0.192" SERVER_PORT = 502 c = ModbusClient() # uncomment this line to see debug message# #c.debug(True) define modbus # server host, port c.host(SERVER_HOST) c.port(SERVER_PORT) #@app.route('/') #def index(): # return 'Hello world' #if __name__ == '__main__': # app.run(debug=True, host='0.0.0.0') # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to "+SERVER_HOST+":"+str(SERVER_PORT)) # if open() is ok, read register (modbus function 0x03) if c.is_open(): # read 10 registers at address 0, store result in regs list regs = c.read_holding_registers(40070, 39) #regs = c.read_holding_registers(40097, 4) # if success display registers if regs: power = regs[13] # 40084 I_AC_Power powersf = regs[14] # 40085 I_AC_Power_SF if powersf>32768: powersf = (65536 - powersf) * -1 power = round(power*(10**powersf),3) lifetime = regs[24]+(65536*regs[23]) # 40094 I_AC_Energy_WH lifetimesf = regs[25] # 40096 I_AC_Energy_WH_SF if lifetimesf>32768: lifetimesf = (66536 - lifetimesf) * -1 lifetime = lifetime*(10**lifetimesf) status = regs[37] # 40108 I_Status #print("reg ad 0 to 9: "+str(regs)) print {"reg 40084= #"+str(regs[15])} print power print powersf print #(str(lifetime) + "wh") print lifetimesf codes = ["NA", "OFF", "SLEEP", "START", "ON", "PROD", "STOP", "FAULT", "SETUP"] #print (codes[status]) now = datetime.now() current_time = now.strftime("%H:%M:%S") #print (current_time + "\t" + str(power) + "wh\t\t" + str(lifetime) + "wh\t" + codes[status]) print ("current=" + str(power) + "&lifetime=" + str(lifetime) + "&status=" + codes[status]) #i = 0 #40108 I_Status 1=OFF, 2=SLEEP, 3=START, 4=ON, 5=PROD, 6=STOP, #7=FAULT, 8=SETUP print("----------------------") #while i < len(regs): #print (str(i+40071) + " : " + str(regs[i])) i += #1 #sleep 2s before next polling and with solaredgemodbus.php i run and output the python script <?php //print "Result of solaredgemodbus.py is:<br />\n"; //$read = escapeshellcmd("python solaredgemodbus.py"); $command = escapeshellcmd('python3 solaredgemodbus.py'); $read = shell_exec($command); if (substr($read, 0,7) == "current") { print $read; /*$ts = time(); $time = date('H:i:s',$ts); $date = date('d-m-Y',$ts); $fp=fopen("output.log","a"); if (fwrite($fp, $time."\t".$date."\t".$read)) { //print "writing data to output.log succes."; } else { //print "writing data to output.log failed."; } fclose ($fp);*/ } else { // make log $ts = time(); $time = date('H:i:s',$ts); $date = date('d-m-Y',$ts); $fp=fopen("output.log","a"); if (fwrite($fp, $time."\t".$date."\t".$read."\n")) { //print "writing data to output.log succes."; } else { //print "writing data to output.log failed."; } fclose ($fp); } ?> and that's the reason i want to eliminate the python-script..... it's too much hassle. for the sunspec specifications just google on sunspec solaredge modbus. Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582662 Share on other sites More sharing options...
requinix Posted November 27, 2020 Share Posted November 27, 2020 Without me trying to write the script myself, I see three options: 1. Dig deeper into the Python code to find out exactly what it does. Specifically, the ModbusClient. Translate everything in there directly to PHP code. Same class names, same method names, same everything. 2. Use a packet sniffer to see what the Python script sends versus what your script (for the same information) sends. 3. Keep the Python script, and write your output.log stuff in Python instead of PHP. Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582666 Share on other sites More sharing options...
rene Posted November 27, 2020 Author Share Posted November 27, 2020 (edited) the pymodbus.py library is somewhere in the os. I just want to write the php code from scratch. I assume the communicating is thru websocket protocol. How hard can it be? How many members this forum have? I hoped that there would be members that had already did something with modbus-tcp protocol or even better....have a solaredge converter at home. Would it be great for those to see there values realtime and being independent from the solaredge-servers? But i place some print "testx<br />\n"; and noticed the script wouldnt passed the write function. and write must be fwrite i think. I also now experimenting with $fp = stream_socket_client("tcp://192.168.0.192:502", $errno, $errstr, 3); but still no output...... which raise the question for me, is it tcp or udp. i thought modbus-tcp is a serial protocol encapsulated in a tcp protocol and it's just sending the codes and after that receiving the codes ..... Unfortunally modbus is not common for non-industry so not much good examples i.c.w. php. But again, it must be possible .......... right? ---------------------------------- btw: this is output from phpversion() ------- Additional Modules Module Name Environment -------- sockets Sockets Supportenabled sodium Edited November 27, 2020 by rene Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582667 Share on other sites More sharing options...
requinix Posted November 27, 2020 Share Posted November 27, 2020 38 minutes ago, rene said: I just want to write the php code from scratch. I assume the communicating is thru websocket protocol. How hard can it be? Hahaha. "I want to build a race car from scratch. It has four tires, a steering wheel that pivots them, and windshield wipers. How hard can it be?" You aren't just writing PHP code. You're writing PHP code that must observe a binary protocol communicating through a TCP socket. That is something 99% of PHP developers never have to think about in their careers. If you have a reliable spec that is relatively easy to understand then perhaps you should start there. Build your code from the ground up by dealing with the little things here and there: a function to send a specific message, a function to read a specific message, a class holding constants for all those magic numbers like 40084 and 40108. Each one of those can be written more or less by itself, and when you have enough of them, you can piece them together into something that actually does the thing you want done. 38 minutes ago, rene said: But i place some print "testx<br />\n"; and noticed the script wouldnt passed the write function. and write must be fwrite i think. Now that is something we're suitable to deal with. If you didn't create it yourself then yes: PHP doesn't have a "write" function. You will be wanting fwrite. 38 minutes ago, rene said: which raise the question for me, is it tcp or udp. The Python script says TCP... If it's not TCP then the initial connection to the device will fail. Which is a really easy thing to notice. And since you haven't noticed it, I imagine it hasn't happened. Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582669 Share on other sites More sharing options...
NotionCommotion Posted November 27, 2020 Share Posted November 27, 2020 As a protocol goes, Modbus is very simple, ascii, really old, and well documented. I've never done anything with Modbus/TCP but did quite a bit with Modbus/RTU (serial) years ago. Pretty much just an address, write/read command, and data with some meta start and end bytes. It is my understanding that Modbus/TCP is pretty much the same thing but wrapped in some sort of IP packet. You will either want to be a master/client or slave/server (master/slave for serial and client/server for IP). For a master/client, you put some bytes on the wire and the device you address either writes that data to itself or returns some data (been about 20 years and forgot the details). Slave/servers listen for the start bytes and then capture some amount of bytes. Pay attention to the meta start and checksum bytes. Assuming you are going IP and want to start from scratch, I would first create either a socket server or client (see https://reactphp.org/socket/#quickstart-example). You will also want to use something like wireshark to better understand the data. Alternatively, you can just search "modbus php" and might find something a little more baked as a starting point. Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582672 Share on other sites More sharing options...
rene Posted November 27, 2020 Author Share Posted November 27, 2020 when you google on modbus php you end up with only one from Jan Krakora which gives me a error about connection which google gives unfortunally not a solution. But i will study that link of reactphp tomorrow. It's indeed something with socket (is there a diffrence with websocket?) ....... Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582675 Share on other sites More sharing options...
rene Posted November 28, 2020 Author Share Posted November 28, 2020 I tried: $host = "192.168.0.192"; $port = 502; // No Timeout set_time_limit(0); $socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n"); socket_connect($socket, $host, $port) or die("Could not connect to server\n"); socket_write($socket, $senddata) or die("Could not send data to server\n"); $result = socket_recv($socket, $buf , 2000 , 0 ); echo "Reply From Server :".strlen($result)." / ".strlen($result)." / ".strlen($buf); socket_close($socket); print "\n\n<br />\ntest1<br />\n"; but i still don't get output .... 11 03 138c6 0027 ea1d Reply From Server :1 / 1 / 0 test1 Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582685 Share on other sites More sharing options...
rene Posted December 3, 2020 Author Share Posted December 3, 2020 was a real brain-cracker but i manage to solves it ...... <=closed => Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582779 Share on other sites More sharing options...
NotionCommotion Posted December 5, 2020 Share Posted December 5, 2020 On 12/3/2020 at 1:08 AM, rene said: was a real brain-cracker but i manage to solves it ...... <=closed => What was the fix? Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582838 Share on other sites More sharing options...
PatM Posted December 10, 2020 Share Posted December 10, 2020 On 12/3/2020 at 7:08 PM, rene said: was a real brain-cracker but i manage to solves it ...... <=closed => Duuude!! Don't just leave us hanging like that. Some of us have an identical issue (no response from the inverter no matter what we try), please add a post indicating how you managed to solve your issue. Quote Link to comment https://forums.phpfreaks.com/topic/311769-reading-modbus-tcp-registers-of-solaredge-inverter/#findComment-1582949 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.