assembly12 / Foxess-T-series-ESPHome-Home-Assistant

Read out Foxess T-Series Inverter to Home Assistant by using ESPHome
34 stars 7 forks source link

Changes in Class (byte length, PV power, FeedIn grid) #2

Closed Henry-Sir closed 1 year ago

Henry-Sir commented 2 years ago

Hi,

As I wrote in the Home Assistant forum, I made some changes. First sorry for late response.

For background: I have installed a Foxess T15 with smart meter (https://forum--foxess-pro.translate.goog/community/foxess-konfiguracja-falownikow/opis/?_x_tr_sl=pl&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp) and 17 solar moduls on one string connected to "String 1" Input (String 2, 3, 4 are not used at the moment) This also makes the inverter have values like loadPower, GridPower, Feedin Power, etc.

My change / enhancements

  1. PV power 1 - 4 sensor via class and not as template sensor. Why: The template sensor is updated every 5 seconds, even if it has no values. I found this unattractive. I also added a PV power sensor (total PV power).

       Sensor *pv1_power = new Sensor;  
       Sensor *pv2_power = new Sensor;  
       Sensor *pv3_power = new Sensor;  
       Sensor *pv4_power = new Sensor;
       Sensor *pv_power = new Sensor;
    
        uint32_t pv1_power_value = pv1_current_value.UInt16 * pv1_voltage_value.UInt16;
        id(pv1_power).publish_state(pv1_power_value);
        ESP_LOGD("custom", "pv1_power: %u" , pv1_power_value);
    
        uint32_t pv2_power_value = pv2_current_value.UInt16 * pv2_voltage_value.UInt16;
        id(pv2_power).publish_state(pv2_power_value);
        ESP_LOGD("custom", "pv2_power: %u" , pv2_power_value);
    
        uint32_t pv3_power_value = pv3_current_value.UInt16 * pv3_voltage_value.UInt16;
        id(pv3_power).publish_state(pv3_power_value);
        ESP_LOGD("custom", "pv3_power: %u" , pv3_power_value);       
    
        uint32_t pv4_power_value = pv4_current_value.UInt16 * pv4_voltage_value.UInt16;
        id(pv4_power).publish_state(pv4_power_value);
        ESP_LOGD("custom", "pv4_power: %u" , pv4_power_value);
    
        uint32_t pv_power_value = pv1_power_value + pv2_power_value + pv3_power_value + pv4_power_value;
        id(pv_power).publish_state(pv_power_value);
        ESP_LOGD("custom", "pv_power: %u" , pv_power_value);       
  2. dynamic code length As already written, the length changes with the software states. Code length is specified byte 8 & 9.

      //make sure at least 9 header bytes are available to check
      if(bytes.size() < 9){
        //ESP_LOGD("custom", "error bytes lower than 9");
        //ESP_LOGD("custom", "bytes size is: %u", bytes.size() );
        continue;
      }
    
      if(bytes[0] != 0x7E || bytes[1] != 0x7E || bytes[2] != 0x02) {
        bytes.erase(bytes.begin()); //remove first byte from buffer
        //buffer will never get above 9 until the header is correct
        //ESP_LOGD("custom", "remove first byte from buffer");
        continue;
      }
      else {
      }
    
        TwoByte data_length_value;
        data_length_value.Byte[0] = bytes[8];
        data_length_value.Byte[1] = bytes[7];
    
        uint16_t protocol_length_value = data_length_value.UInt16 + 13;
        //ESP_LOGD("custom", "data_length: %u", data_length_value.UInt16 );
        //ESP_LOGD("custom", "protocol_length: %u", protocol_length_value);
    
      if(bytes.size() == protocol_length_value) { 
    
        //ESP_LOGD("custom", "bytes size is: %u", bytes.size() );
    
        if(bytes[protocol_length_value-1] != 0xE7 || bytes[protocol_length_value-2] != 0xE7) {
          ESP_LOGD("custom", "error in reading message");
          ESP_LOGD("custom", "second to last byte: %X", bytes[protocol_length_value-2] );
          ESP_LOGD("custom", "last byte: %X", bytes[protocol_length_value-1] );
          bytes.clear();
    
          continue;
        }
  3. sensor GridPower & LoadPower gives values close to 65536. According to configuration from inverter exported power may be between 0 - 32767 W. Since I have seen values close to 65536 in the Home Assitant, I had wondered. These values are then the negative values of the grid, so the power that is fed into the grid. -> Value from 0 to 32767 is the power that is consumed by the grid. (positive value) -> Value from 65535 to 32768 is the power that is fed into the grid. (negative value) I also added a FeedIn power sensor.

    See the values from ESP and the Foxess cloud

    Before the change

    grafik grafik

    After the change

    grafik grafik

        TwoByte grid_power_value;
        grid_power_value.Byte[0] = bytes[10];
        grid_power_value.Byte[1] = bytes[9];
        //id(grid_power).publish_state(grid_power_value.UInt16);
        //ESP_LOGD("custom", "grid_power: %u" , grid_power_value.UInt16);
    
        int16_t grid_power_value_int;
        int16_t feedin_power_value;
        if(grid_power_value.UInt16 > 32768) {
          //Feed in
          grid_power_value_int = 65536 - grid_power_value.UInt16 * -1;
          feedin_power_value = 65536 - grid_power_value.UInt16;
        }
        else {
          //Consumption
          grid_power_value_int = grid_power_value.UInt16;
          feedin_power_value = 0;
        }
        id(grid_power).publish_state(grid_power_value_int);
        ESP_LOGD("custom", "grid_power: %i" , grid_power_value_int);
        id(feedin_power).publish_state(feedin_power_value);
        ESP_LOGD("custom", "grid_power: %i" , feedin_power_value);
    
        TwoByte loads_power_value;
        loads_power_value.Byte[0] = bytes[14];
        loads_power_value.Byte[1] = bytes[13];
    
        int16_t loads_power_value_int;
        if(grid_power_value.UInt16 > 32768) {
          //Feed in
          loads_power_value_int = 65536 - grid_power_value.UInt16 * -1;
        }
        else {
          //Consumption
          loads_power_value_int = grid_power_value.UInt16FeedIn;
        }
        id(loads_power).publish_state(loads_power_value_int);
        ESP_LOGD("custom", "loads_power: %i" , loads_power_value_int);
  4. Added a few sensors

    • Feedin Generation1
    • Feedin Generation2 -> but both are always empty
    • Timestamp Epoch (Epoch = UNIX timestamp)

YAML and the .h file

foxess-inverter.yaml.txt foxess_t_series.h.txt

assembly12 commented 2 years ago

Hi Henry,

sorry it took me so long to get back to you. First off, thank you for your extensive work!

I'll adress you modifications seperately:

  1. definitely on board with this one! I've allready implemented this in the code, if that's ok by you. My old way of doing it was still a remnant of a completely other route i was taking previously.

  2. dynamic code length was allready added by the time you posted this. So the implementation is a bit different from yours, but it should do the same thing.

  3. I don't have a smart meter attached, so i really can't confirm this. But if it's working for you, i'll gladly add it to the .h file. However where you say if(grid_power_value.UInt16 > 32768) { , shouldn't it be if(grid_power_value.UInt16 > 32767) { ?

  4. My ESP was allready struggling with the amount of sensors being updated at the same time. So i didn't add anymore (i'll probably even remove some of the less interesting ones later on). Feedin generation is always 0 for me and i'm not even sure what it's supposed to represent. The timestamp is basicly generated by HA.