March 6, 20179 yr 1 minute ago, Praveen said: Mine is not a off grid inverter. Its a hybrid inverter with battery backup. My understanding is, WatchPower is for off-grid inverter. What is the make and model of your inverter?
March 6, 20179 yr Author 2 minutes ago, Don said: What is the make and model of your inverter? It is Infinisolar V model. Attached the device info view from WatchPower.
March 6, 20179 yr @Praveen, you need to use WatchPower, whether you are Off-Grid, On-Grid or Hybrid, does not matter. SolarPower will not work with your inverter.
March 6, 20179 yr Author 2 minutes ago, Don said: @Praveen, you need to use WatchPower, whether you are Off-Grid, On-Grid or Hybrid, does not matter. SolarPower will not work with your inverter. I was trying to view the energy exported by the inverter to the Grid. WatchPower doesn't show those information. Is there any special privileges need to open the view?
March 6, 20179 yr Yes, I think the functionality you are looking for is in SolarPower and not in WatchPower. We all started off with WatchPower. Play around with it and very soon realize it is very basic and not really what we require. That is where ICC and similar programs come in. They do have that functionality.
March 7, 20179 yr Author Update: I used the solarmonitor software from http://www.opti-solar.com/english/download.en.html and it worked. It seems the protocol is called P18. I will find more details about this soon.
March 15, 20179 yr Author Figured out communication with inverter using python. Facing a weird problem. SolarPower software sends command "PI" command and gets back "P18" as value. When I send the same, I get back "(NAKss". However, sending "QPI" command returns "P30". I am assuming the inverter is defaulting to P30 protocol because of something wrong with the command I send. Is there any encoding/transformation to be done before sending command to the inverter?
March 15, 20179 yr 2 hours ago, Praveen said: Is there any encoding/transformation to be done before sending command to the inverter? Yes, there is the CRC (two Cyclic Redundancy Check characters). That's why the NAK comes back as NAKss; the "ss" part is the two-byte (two-character) CRC. There are a very few commands that don't require a CRC, and I think that QPI must be one of them, since it doesn't send back a checksum, just the 6 characters "(QPI30" and a carriage return. Try sending the CRC with the PI command. In this case, it's 0xD752. The second character is just "R", but the first is "W" with the sign bit set. Comms programs differ in how to send such characters. Some background on using commands (for the non-V Axpert, but the principles should be the same). The end of this post has how to send 8-bit characters using the Windows program TeraTerm. C source code for generating CRCs. A Windows applet for generating the CRCs more painlessly.
March 15, 20179 yr You can gain some insight on using the python struct module to pack binary data for the inverter.Together with pyserial that should be all you need. You can glean some examples here (this is for the mk2, but similar).
March 15, 20179 yr Author I do have CRC implemented now. I am validating CRC for every output. Also sending CRC bit for every input I send to the inverter. The problem is, the SolarPower software sends the command "PI"(from java) and it somehow gets translated to "^P005PIq..005PIq" while sending to inverter.(It sends two 8 byte hex -> 5E 50 30 30 35 50 49 71 8B 0D 30 30 35 50 49 71). A CRC doesn't explain this translation. I am thinking there is some function(in the libUSBDevice.so file) which does this translation. I want to manually send this hex message to the inverter. Is there any utility where I can directly send hex bytes to inverter and see the output? I have attached the USB communication between inverter and SolarPower 1.21 software(See the sequence id "0910"). usbread.html
March 15, 20179 yr On 2017/03/16 at 5:35 AM, Praveen said: Is there any utility where I can directly send hex bytes to inverter and see the output? My understanding is that when it is set up, USB comms is just the same as serial comms, including being as slow as 2400 bps serial. There is then a virtual comm port like comm12. [ Edit: this isn't quite right; see Gnome's reply. ] So then you can use any serial comms program like TeraTerm. [ Edit: and this last part is completely wrong. ] But the trick is in setting it up. I never figured it out, and I've never seen it documented anywhere. Edited December 9, 20187 yr by Coulomb
March 15, 20179 yr Author I looked for a COM device in device manager(in a windows machine) when connected the inverter and I couldn't find one.
March 15, 20179 yr 22 minutes ago, Praveen said: I looked for a COM device in device manager(in a windows machine) when connected the inverter and I couldn't find one. Yes, it's not that simple, and I don't get why there isn't one visible. I didn't try hard to sort it out.
March 16, 20179 yr 10 hours ago, Praveen said: I do have CRC implemented now. I am validating CRC for every output. Also sending CRC bit for every input I send to the inverter. The problem is, the SolarPower software sends the command "PI"(from java) and it somehow gets translated to "^P005PIq..005PIq" while sending to inverter.(It sends two 8 byte hex -> 5E 50 30 30 35 50 49 71 8B 0D 30 30 35 50 49 71). A CRC doesn't explain this translation. I am thinking there is some function(in the libUSBDevice.so file) which does this translation. I want to manually send this hex message to the inverter. Is there any utility where I can directly send hex bytes to inverter and see the output? I have attached the USB communication between inverter and SolarPower 1.21 software(See the sequence id "0910"). usbread.html OK, below is the simplified version of my function to built the command strings, it is in Delphi, but I'm sure if you are not familiar with Delphi, google will be able to assist. You get 2 command types (CmdType in the function below) namely Poll commands and Set commands, a poll command is used to inquire something from the inverter and a set command is used to set something to the inverter (adjust a setting). The reply to a poll command will be a value depending on what you've requested and the reply to a set command will either be ACK (acknowledge) or NAK (not acknowledge). All responses might or might not include the security string and/or crc check-sum, depending on the inverter model - see below. Some inverter models require the CRC check-sum (UseCRC in function below) added to command strings and will sometimes include the crc check-sums added to the responses. Some inverter models require the "security string" (UseSec in function below) added to the command strings and will sometimes include the "security strings" added to the responses. So you have to start off by sending the the PI and QPI commands to the inverter with different combinations of UseCRC and UseSec until you get a valid response from the inverter. Once you get a valid response you'll have to use that specific combination of UseCRC and UseSec for all other commands to that inverter. A valid response (to a PI or QPI command) from the inverter will be something like ^D00517Êì or (PI16œ¯ which indicates the model number of the inverter, in this case 17 ( ^D00517Êì ) or 16 ( (PI16œ¯ ). So you will use the function below with the following parameters until you get a valid response from the inverter: BuildCommandString('PI', ctPoll, False, False) BuildCommandString('PI', ctPoll, False, True) BuildCommandString('PI', ctPoll, True, False) BuildCommandString('PI', ctPoll, True, True) BuildCommandString('QPI', ctPoll, False, False) BuildCommandString('QPI', ctPoll, False, True) BuildCommandString('QPI', ctPoll, True, False) BuildCommandString('QPI', ctPoll, True, True) As example: If you got a valid response from the inverter after sending the command from BuildCommandString('PI', ctPoll, True, False), you don't have to send any more PI or QPI commands to the inverter (because you received a valid response) and then you have to use the setting for UseCRC and UseSec which gave you a valid response for all other commands for that inverter, as explained above. So in this example each and every command send to this specific inverter needs to include the CRC checksum, but not the "security string", because you received a valid response by sending a command with UseCRC = True and UseSec = False. So examples of other poll commands to this inverter will be: BuildCommandString('QMD', ctPoll, True, False) BuildCommandString('QVFW', ctPoll, True, False) BuildCommandString('QFLAG', ctPoll, True, False) BuildCommandString('QPIGS', ctPoll, True, False) TIP: Sometimes data corruption, noise on the lines, partial old commands on the input-buffer of the inverter etc. prevents the inverter from returning a valid response and therefore it is good practice to send each command up to 3 consecutive times, obviously if you've received a valid response after the first or second time you're sending the command, you won't send it again. type TCommandType = (ctPoll, ctSet); function BuildCommandString(InpStr: string; CmdType: TCommandType; UseCRC, UseSec: boolean): string; function PadInteger(Val, FinalStrLen: integer): string; begin Result := IntToStr(Val); while Length(Result) < FinalStrLen do Result := '0'+Result; end; function AddSetHeader(DataToSend: string; CRCLength: integer): string; begin Result := '^S'+PadInteger(Length(DataToSend)+CRCLength+1, 3)+DataToSend; end; function AddPollHeader(DataToSend: string; CRCLength: integer): string; begin Result := '^P'+PadInteger(Length(DataToSend)+CRCLength+1, 3)+DataToSend; end; function GetCRC(ptr: pchar; len: word): word; const crc_ta: array [0..255] of word = ($0000, $1021, $2042, $3063, $4084, $50A5, $60C6, $70E7, $8108, $9129, $A14A, $B16B, $C18C, $D1AD, $E1CE, $F1EF, $1231, $0210, $3273, $2252, $52B5, $4294, $72F7, $62D6, $9339, $8318, $B37B, $A35A, $D3BD, $C39C, $F3FF, $E3DE, $2462, $3443, $0420, $1401, $64E6, $74C7, $44A4, $5485, $A56A, $B54B, $8528, $9509, $E5EE, $F5CF, $C5AC, $D58D, $3653, $2672, $1611, $0630, $76D7, $66F6, $5695, $46B4, $B75B, $A77A, $9719, $8738, $F7DF, $E7FE, $D79D, $C7BC, $48C4, $58E5, $6886, $78A7, $0840, $1861, $2802, $3823, $C9CC, $D9ED, $E98E, $F9AF, $8948, $9969, $A90A, $B92B, $5AF5, $4AD4, $7AB7, $6A96, $1A71, $0A50, $3A33, $2A12, $DBFD, $CBDC, $FBBF, $EB9E, $9B79, $8B58, $BB3B, $AB1A, $6CA6, $7C87, $4CE4, $5CC5, $2C22, $3C03, $0C60, $1C41, $EDAE, $FD8F, $CDEC, $DDCD, $AD2A, $BD0B, $8D68, $9D49, $7E97, $6EB6, $5ED5, $4EF4, $3E13, $2E32, $1E51, $0E70, $FF9F, $EFBE, $DFDD, $CFFC, $BF1B, $AF3A, $9F59, $8F78, $9188, $81A9, $B1CA, $A1EB, $D10C, $C12D, $F14E, $E16F, $1080, $00A1, $30C2, $20E3, $5004, $4025, $7046, $6067, $83B9, $9398, $A3FB, $B3DA, $C33D, $D31C, $E37F, $F35E, $02B1, $1290, $22F3, $32D2, $4235, $5214, $6277, $7256, $B5EA, $A5CB, $95A8, $8589, $F56E, $E54F, $D52C, $C50D, $34E2, $24C3, $14A0, $0481, $7466, $6447, $5424, $4405, $A7DB, $B7FA, $8799, $97B8, $E75F, $F77E, $C71D, $D73C, $26D3, $36F2, $0691, $16B0, $6657, $7676, $4615, $5634, $D94C, $C96D, $F90E, $E92F, $99C8, $89E9, $B98A, $A9AB, $5844, $4865, $7806, $6827, $18C0, $08E1, $3882, $28A3, $CB7D, $DB5C, $EB3F, $FB1E, $8BF9, $9BD8, $ABBB, $BB9A, $4A75, $5A54, $6A37, $7A16, $0AF1, $1AD0, $2AB3, $3A92, $FD2E, $ED0F, $DD6C, $CD4D, $BDAA, $AD8B, $9DE8, $8DC9, $7C26, $6C07, $5C64, $4C45, $3CA2, $2C83, $1CE0, $0CC1, $EF1F, $FF3E, $CF5D, $DF7C, $AF9B, $BFBA, $8FD9, $9FF8, $6E17, $7E36, $4E55, $5E74, $2E93, $3EB2, $0ED1, $1EF0); var crc, bCRCLow, bCRCHign, tmp: word; da: byte; begin crc := 0; while (len <> 0) do begin len := len - 1; da := $FF and ($FF and (crc shr 8)) shr 4; crc := crc shl 4; crc := crc xor (crc_ta[$FF and (da xor (Ord(ptr^) shr 4))]); da := $FF and ($FF and (crc shr 8)) shr 4; crc := crc shl 4; tmp := $FF and (da xor (Ord(ptr^) and $F)); crc := crc xor crc_ta[tmp]; ptr := ptr + 1; end; bCRCLow := $FF and crc; bCRCHign := $FF and (crc shr 8); if (bCRCLow = $28) or (bCRCLow = $0D) or (bCRCLow = $0A) then bCRCLow := bCRCLow+1; if (bCRCHign = $28) or (bCRCHign = $0D) or (bCRCHign = $0A) then bCRCHign := bCRCHign+1; crc := ($FF and bCRCHign) shl 8; crc := crc+bCRCLow; Result := crc; end; begin Result := ''; if Trim(InpStr) = '' then Exit; if UseCRC then begin if UseSec then begin if CmdType = ctPoll then InpStr := AddPollHeader(InpStr, 2) else InpStr := AddSetHeader(InpStr, 2); end; Result := InpStr+HexStrToStr(Format('%.4x', [GetCRC(PChar(InpStr), Length(InpStr))])); end else begin if UseSec then begin if CmdType = ctPoll then Result := AddPollHeader(InpStr, 0) else Result := AddSetHeader(InpStr, 0); end else Result := InpStr; end; end;
March 16, 20179 yr Author @superdiy This is exactly what I was looking. Just run the command through this code in my mind and getting the expected output. I will implement this and write my findings here. Thank you very much.
September 21, 20187 yr Hi! Dunno if someone posted the protocol documentation for PI18 already, but here it is (maybe again). Infini-Solar V protocol 20170926(PI18).pdf
December 6, 20187 yr On 2017/03/16 at 12:09 PM, Praveen said: @superdiy This is exactly what I was looking. Just run the command through this code in my mind and getting the expected output. I will implement this and write my findings here. Thank you very much. @superdiy I have a question. Looking at Delphi code above, it looks that appending the final <CR> at the end of InpStr is the final step just before sending it to the inverter, right? Because I don't see this is being done at least in this code. Which also means that the CRC calculation should be done for every thing except the last <CR> character. As an example CRC should be calculated for ^P005PI and then after appending it as ^P005PI<CRC> then CR should be appended such as ^P005PI<CRC><CR>. Please confirm.
December 6, 20187 yr Author I do not remember this exactly. You can look at my code here. https://github.com/praveenkv1988/solarmon
December 6, 20187 yr 9 hours ago, Nadeem Ahmed said: CRC should be calculated for ^P005PI and then after appending it as ^P005PI<CRC> then CR should be appended such as ^P005PI<CRC><CR>. That's how it works for Axperts.
December 8, 20187 yr On 2017/03/16 at 12:19 AM, Coulomb said: My understanding is that when it is set up, USB comms is just the same as serial comms, including being as slow as 2400 bps serial. There is then a virtual comm port like comm12. So then you can use any serial comms program like TeraTerm. But the trick is in setting it up. I never figured it out, and I've never seen it documented anywhere. It has the same latency and bandwidth as the serial because it seems to be limited by the underlying processor. But the protocol is different for sure. It is just a USB HID device that accepts 8 bytes at a time. In Linux you just open the device (/dev/hidraw0) as a file and write to it and read from it. You'll need to set it as non-blocking read and write. HIDApi will allow you to communicate with it regardless of your operating system Edited December 8, 20187 yr by Gnome
December 10, 20187 yr On 2017/03/16 at 12:19 AM, Coulomb said: My understanding is that when it is set up, USB comms is just the same as serial comms, including being as slow as 2400 bps serial. There is then a virtual comm port like comm12. [ Edit: this isn't quite right; see Gnome's reply. ] So then you can use any serial comms program like TeraTerm. [ Edit: and this last part is completely wrong. ] But the trick is in setting it up. I never figured it out, and I've never seen it documented anywhere. I was under the impression USB is faster than serial, i.e. not 2400bps anymore. And it should give full duplex communications, which serial doesn't (or, at least do properly)
December 10, 20187 yr 31 minutes ago, SilverNodashi said: I was under the impression USB is faster than serial, i.e. not 2400bps anymore. And it should give full duplex communications, which serial doesn't (or, at least do properly) From the PC up to the USB-serial chip it is faster. From there onwards it runs at normal serial speeds. Granted, it is usually capable of much higher speeds, eg FTDI chips can go up to 12 mbit if I recall correctly (there are some caveats I don't recall exactly). I did some of my own testing a few years ago, and found out that it is pretty silly to go above around 500kbaud, at that point all sorts of other software layers starts bottle-necking you. Nevertheless, many designs still use slower speeds because they can get away with it. More dependable, and the volume of data is super low in any case. Some designs kick up the speed when doing firmware updates.
December 10, 20187 yr 7 minutes ago, plonkster said: From the PC up to the USB-serial chip it is faster. From there onwards it runs at normal serial speeds. Granted, it is usually capable of much higher speeds, eg FTDI chips can go up to 12 mbit if I recall correctly (there are some caveats I don't recall exactly). I did some of my own testing a few years ago, and found out that it is pretty silly to go above around 500kbaud, at that point all sorts of other software layers starts bottle-necking you. Nevertheless, many designs still use slower speeds because they can get away with it. More dependable, and the volume of data is super low in any case. Some designs kick up the speed when doing firmware updates. In the case of electronics like an inverter I would thinks it's still quicker than serial communications? On an Arduino, for example, I can communicate much quicker than 2400pbs, and in fact in many instances (like a GPS tracker) anything below 115200 is too slow to get all the data quick enough. Microprocessors have real time processing. An Arduino, as example again, runs at 16Mhz - that's 1/16000000 of a second that it could run an instruction. Some instructions run on two cycles, and some on three, but that's still super fast. Computers are faster but due to multiprocessing things could happen slower, depending on how many threads, and cores are being used.
December 10, 20187 yr 1 hour ago, SilverNodashi said: I was under the impression USB is faster than serial, i.e. not 2400bps anymore. And it should give full duplex communications, which serial doesn't (or, at least do properly) Several things to note about USB: USB doesn't have any kind of synchronous protocol Sure the actual data transfer is full-duplex but the protocol itself is asynchronous model You tell the USB controller to send packets to the device and it'll send it when it is good and ready The device then responds to the controller which will notify VIA an IRQ again when it is ready USB has higher latency and unpredictable latency Because of the above, USB is not used for time critical applications like GPS and time sources RS232 is still considered superior in this space Now as for RS232 it does not have the bandwidth that USB has but it has lower latency. Other than parallel there is no lower latency protocol (OK there is SPI but it isn't well supported). Not only that, the actual latency is 100% predictable and repeatable. Meaning if you are running a real-time operating system (which we are not), you can exactly time your RS232 on clock cycles with relative accuracy (some accommodation must be made for clock drift on the order of μs, but compared to USB it is orders of magnitude better). Lastly, the Axpert device isn't limited by the speed of the RS232, it is limited by the device. 2400bps is as good as it gets. I've got a library I wrote that allows both USB & RS232 to be used for Axpert. The library is otherwise using the exact same code. The total latency is exactly the same between the two ± 15ms. I found USB to be less reliable on the Axpert. Doing things wrong on USB I was able to cause the inverter to stop responding to commands which required a restart (mind you the inverter still ran normally). But on RS232 it was more reliable. Edited December 10, 20187 yr by Gnome
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.