RS232 read data from MPP-Solar inverter

@athersaleem waiting for you videos to be online :slight_smile:

I tried the testpip.ino code, and as I initially wired the TX and RX to RS22 module, I got no response, after I swapped the RX with X i got some response, but it's not what I expected

Packet Sent - command 1
Got good packet!
QPIGS
SYSTEM STATUS
Inverter Fault: -
Bus Over Voltage: -
Bus Under Voltage: -
Bus Soft Fail: -
Line Fail: -
OPV Short: -
Inverter Voltage Too Low: -
Inverter Voltage Too High: -
Over Temperature: -
Fan Locked: -
Battery Voltage Too High: -
Battery Low Alarm: -
Battery Under Shutdown: 
-
Overload - -
EEPROM Fault: 
-
Packet Sent - command 0
Got good packet!
QPIWS
gridVoltage: PIWS
gridFrequency: 
acOutput: 
acFrequency: 
acApparentPower: 
acActivePower: 
loadPercent: 
busVoltage: 
batteryVoltage: 
batteryChargeCurrent: 
batteryCharge: 
inverterTemperature: 
PVCurrent: 
PVVoltage: 
PVPower: 0

The response doesn't contain any data, I just see the command QPIWS.

The wiring can be seen in the photo bellow:

Grey wire from RS232 to D16, and WHITE wire to D17 of Mega 2560 PRO mini board (Serial2) and also have the LAN connector in the photo, PIN1 orange-white, PIN2 blue, PIN8 green-white.

Thank you for any more advices.

PS. Here is the CODE I TRIED (just modified to use Serial2 on my MEGA)

uint8_t pipInputBuf[500];

int pipInputPointer = 0;

// Structure to store the data from the PIP4048
struct pipVals_t {
  char gridVoltage[16];
  char gridFrequency[16];
  char acOutput[16];
  char acFrequency[16];
  char acApparentPower[16];
  char acActivePower[16];
  char loadPercent[16];
  char busVoltage[16];
  char batteryVoltage[16];
  char batteryChargeCurrent[16];
  char batteryCharge[16];
  char inverterTemperature[16];
  char PVCurrent[16];
  char PVVoltage[16];
} pipVals;

// Some useful PIP commands
struct pipCommands_t {
  unsigned char qpigs[5];
  unsigned char qpiws[5];
} pipCommands = {{'Q', 'P', 'I', 'G', 'S'}, {'Q', 'P', 'I', 'W', 'S'}};

int whichPIPCommand = 0, lastPIPCommand = 0; // which PIP values to read

void setup()
{
  Serial.begin(115200); //Serial Monitor Console Baud Setting
  Serial2.begin(2400, SERIAL_8N1); //MPP-Solar inverter Baud Setting(http://www.offgrid.casa/wp-content/uploads/2017/10/HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf)
}


void loop ()
{
  uint16_t crc;
  char *val;
  char pipstatus[40];

  delay(5000); // read every 5 seconds

  // Send alternating commands to PIP - QPIGS then QPIWS
  Serial.print("Packet Sent - command ");
  Serial.println(whichPIPCommand);

  switch (whichPIPCommand) {
    case 0:
      pipSend(pipCommands.qpigs, sizeof(pipCommands.qpigs));
      lastPIPCommand = 0;
      whichPIPCommand = 1; // Next command
    break;
    
    case 1:
      pipSend(pipCommands.qpiws, sizeof(pipCommands.qpiws));
      lastPIPCommand = 1;
      whichPIPCommand = 0; // Next command
    break;
  }


  // Check any return from the pip4048
  int i = processPipInput(&crc);
  if (i > 0) // Got a good packet
  {
    Serial.println("Got good packet!");
    Serial.println((char *) pipInputBuf);

    switch (lastPIPCommand) // Which paccket are we expecting?
    {
      case 0: // QPIGS
        // Now split the packet into the values
        val = strtok((char *) pipInputBuf, " "); // get the first value
        strcpy(pipVals.gridVoltage, val + 1); // Skip the initial '('

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.gridFrequency, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.acOutput, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.acFrequency, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.acApparentPower, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.acActivePower, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.loadPercent, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.busVoltage, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.batteryVoltage, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.batteryChargeCurrent, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.batteryCharge, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.inverterTemperature, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.PVCurrent, val);

        val = strtok(0, " "); // Get the next value
        strcpy(pipVals.PVVoltage, val);


        // Print out readings
        Serial.print("gridVoltage: ");
        Serial.println(pipVals.gridVoltage);
        Serial.print("gridFrequency: ");
        Serial.println(pipVals.gridFrequency);
        Serial.print("acOutput: ");
        Serial.println(pipVals.acOutput);
        Serial.print("acFrequency: ");
        Serial.println(pipVals.acFrequency);
        Serial.print("acApparentPower: ");
        Serial.println(pipVals.acApparentPower);
        Serial.print("acActivePower: ");
        Serial.println(pipVals.acActivePower);
        Serial.print("loadPercent: ");
        Serial.println(pipVals.loadPercent);
        Serial.print("busVoltage: ");
        Serial.println(pipVals.busVoltage);
        Serial.print("batteryVoltage: ");
        Serial.println(pipVals.batteryVoltage);
        Serial.print("batteryChargeCurrent: ");
        Serial.println(pipVals.batteryChargeCurrent);
        Serial.print("batteryCharge: ");
        Serial.println(pipVals.batteryCharge);
        Serial.print("inverterTemperature: ");
        Serial.println(pipVals.inverterTemperature);
        Serial.print("PVCurrent: ");
        Serial.println(pipVals.PVCurrent);
        Serial.print("PVVoltage: ");
        Serial.println(pipVals.PVVoltage);

        // Calculate PV Power
        int I, V;
        I = atoi(pipVals.PVCurrent);
        V = atoi(pipVals.PVVoltage);
        Serial.print("PVPower: ");
        Serial.println(I * V);
      break;

      case 1: // QPIWS
        val = strtok((char *) pipInputBuf, " "); // get the first value
        strcpy(pipstatus, val + 1); // Skip the initial '(' - make a copy of the returned stricg for processing
        Serial.println("SYSTEM STATUS");
        // Now send the various PIP status messages
        Serial.print("Inverter Fault: ");
        if (pipstatus[1] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Bus Over Voltage: ");
        if (pipstatus[2] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Bus Under Voltage: ");
        if (pipstatus[3] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Bus Soft Fail: ");
        if (pipstatus[4] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Line Fail: ");
        if (pipstatus[5] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("OPV Short: ");
        if (pipstatus[6] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Inverter Voltage Too Low: ");
        if (pipstatus[7] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Inverter Voltage Too High: ");
        if (pipstatus[8] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Over Temperature: ");
        if (pipstatus[9] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Fan Locked: ");
        if (pipstatus[10] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Battery Voltage Too High: ");
        if (pipstatus[11] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Battery Low Alarm: ");
        if (pipstatus[12] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.println("Battery Under Shutdown: ");
        if (pipstatus[14] == '1') Serial.println("FAULT");
        else Serial.println("-");

        Serial.print("Overload - ");
        if ((pipstatus[16] == '1') && (pipstatus[1] == '0')) Serial.println(" Warning");
        else if ((pipstatus[16] == '1') && (pipstatus[1] == '1')) Serial.println(" FAULT");
        else Serial.println("-");

        Serial.println("EEPROM Fault: ");
        if (pipstatus[17] == '1') Serial.println("FAULT");
        else Serial.println("-");

      break;
    }
  }

  if (i == -1) // Got a bad packet
  {
    Serial.println("Got BAD packet");
    Serial.println((char *) pipInputBuf);
  }
}


// Check for input from Serial2, put it into a buffer and then return the 
// buffer length if a <cr> has been detected and the packet is valid,
// 0 if <cr> hasn't yet been detected and -1 if an invalid crc has been sent

int processPipInput(uint16_t *retCrc)
{
  uint8_t pipChar;
  uint16_t newCrc;

  while (Serial2.available()) // Got any input?
  {
    if ((pipChar = Serial2.read()) != 0x0d) // Read the byte
    {
      pipInputBuf[pipInputPointer++] = pipChar; // Not a <cr>
    }
    else
    { // Got a <cr>, calculate the crc
      newCrc = cal_crc_half(pipInputBuf, pipInputPointer - 2);
      if (newCrc == ((((pipInputBuf[pipInputPointer - 2]) << 8) & 0xff00) | (pipInputBuf[pipInputPointer - 1] & 0xff))) // Good crc
      {
        int8_t i = pipInputPointer - 2;
        pipInputBuf[i] = 0; // Terminate the string in the input buffer, overwriting the crc - so it can  easily be printed out
        pipInputPointer = 0; // Zero the pointer ready for the next packet
        *retCrc = newCrc; // Return the buffer CRC
        return (i); // Return length of buffer
      }
      else
      {
        pipInputBuf[pipInputPointer + 1] = 0; // Terminate the string for display...keep the crc in place for checking
        pipInputPointer = 0;
        return (-1); // Indicate bad crc
      }
    }
  }

  return (0); // packet not yet finished
}


// Send a packet to the pip4048

void pipSend(uint8_t txArray[], int length)
{
  int crc = cal_crc_half(txArray, length);

  Serial2.write(txArray, length);
  Serial2.write((crc >> 8) & 0xff);
  Serial2.write(crc & 0xff);
  Serial2.write(0x0d);
}


uint16_t cal_crc_half(uint8_t  *pin, uint8_t len)
{

  uint16_t crc;

  uint8_t da;
  uint8_t  *ptr;
  uint8_t bCRCHign;
  uint8_t bCRCLow;

  uint16_t crc_ta[16] =
  {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,

    0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef
  };
  ptr = pin;
  crc = 0;

  while (len-- != 0)
  {
    da = ((uint8_t)(crc >> 8)) >> 4;

    crc <<= 4;

    crc ^= crc_ta[da ^ (*ptr >> 4)];

    da = ((uint8_t)(crc >> 8)) >> 4;

    crc <<= 4;

    crc ^= crc_ta[da ^ (*ptr & 0x0f)];

    ptr++;
  }
  bCRCLow = crc;

  bCRCHign = (uint8_t)(crc >> 8);

  if (bCRCLow == 0x28 || bCRCLow == 0x0d || bCRCLow == 0x0a)

  {
    bCRCLow++;
  }
  if (bCRCHign == 0x28 || bCRCHign == 0x0d || bCRCHign == 0x0a)

  {
    bCRCHign++;
  }
  crc = ((uint8_t)bCRCHign) << 8;
  crc += bCRCLow;
  return (crc);
}