ESPHome UART & Pylontech RS232 and displaying as sensor

Good afternoon!
First off apologies, I’ve been struggling for days on this and I would really appreciate some help - if you can.

I have a esphome running on a esp8266 with an rs232 board connected to a Pylontech battery.

I am looking to setup some sensors that will show the status of the battery when I send commands like “info” or “pwr” to it via UART
Right now, in my yaml I have this

uart:
  id: uart_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
  rx_buffer_size: 2048
  debug:
    direction: BOTH
    dummy_receiver: false
    after:
      delimiter: "\n"
    sequence:
      - lambda: UARTDebug::log_string(direction, bytes);

interval:
  - interval: 10s
    then:
      # PI30/PI30MAX/PI30REVO/PI41
      - logger.log:
          level: INFO
          format: "Testing PI30/PI30MAX/PI30REVO/PI41 commands..."
      - logger.log:
          level: INFO
          format: "This is the set of commands supported by the pipsolar component!"
        # For escape characters, you must use double quotes!
      - uart.write: "info\r\n"
      - delay: 1s

in return I get this every 10 seconds which is great

Time	level	Tag	Message
13:59:53	[I]	[main:087]	
Testing PI30/PI30MAX/PI30REVO/PI41 commands...
13:59:53	[I]	[main:090]	
This is the set of commands supported by the pipsolar component!
13:59:53	[D]	[uart_debug:158]	
>>> "info\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "info\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\r@\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rDevice address      : 1\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rManufacturer        : Pylon\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rDevice name         : US2\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rBoard version       : PHANTOMS\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rMain Soft version   : B65.12\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rSoft  version       : V2.3\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rBoot  version       : V2.0\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rComm version        : V2.0\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rRelease Date        : 19-06-22\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rBarcode             : PPT12345\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\r\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rSpecification       : 48V/50AH\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rCell Number         : 15\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rMax Dischg Curr     : -100000mA\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rMax Charge Curr     : 102000mA\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rEPONPort rate       : 1200\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rConsole Port rate   : 115200\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rCommand completed successfully\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\r$$\r\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rpylon>\n"
13:59:53	[D]	[uart_debug:158]	
<<< "\rpylon>"

What I am struggling is to get (for example) is a sensor that sets the “Manufacturer” as “Pytlontech”

The idea is at some point to have it pull battery % for example and display that in HA.

Any help would be amazing!

@Dilbert66 Howzit bud, sorry to ask out of the blue, but I know you are really good at this stuff from your awesome DSC integration, would you be able to give us any pointers?

1 Like

Take a look at this post. To extract values from the sample INFO output you provided, the code would look something like this:

int intValue=0;
if (sscanf(str.c_str(), "\rCell Number         :%d", &intValue) == 1 ) {
  id(sensor_id_cells).publish_state(intValue); 
}

   

Hi @mulcmu
Thanks so much for that!

I’m starting to get this working (kind of)
I am struggling with getting text into this though, I’ve started with “Manufacturer” and I’ve got stuck.
Could you give me any pointers?

uart:
  id: uart_bus
  tx_pin: 1
  rx_pin: 3
  baud_rate: 115200
  rx_buffer_size: 2048
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\r\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);  // Still log the data
          
          int sensorID = 0;
          float sensorTEMP = 0; 
          
          // Example to convert uart text to string
          std::string str(bytes.begin(), bytes.end());

          // Watch for potential problems with non-printable or special characters in string
          id(rawString).publish_state(str.c_str());
          
          int intValue = 0;
          if (sscanf(str.c_str(), "\rCell Number         :%d", &intValue) == 1) {
            id(infocells).publish_state(intValue); 
          }
          
          if (sscanf(str.c_str(), "\rMax Charge Curr         :%d", &intValue) == 1) {
            id(infomaxchargecurr).publish_state(intValue); 
          }

          if (sscanf(str.c_str(), "\rDevice address         :%d", &intValue) == 1) {
            id(infodeviceaddress).publish_state(intValue); 
          }

          std::string manufacturer;
          char tempManufacturer[32];
          if (sscanf(str.c_str(), "\rManufacturer        :%31s", tempManufacturer) == 1) {
            manufacturer = tempManufacturer;
            id(infomanufacturer).publish_state(manufacturer.c_str()); 
          }

sensor:
  - platform: template
    name: "INFO - Cells"
    id: infocells
  - platform: template
    name: "INFO - Max Charge Current"
    id: infomaxchargecurr
  - platform: template
    name: "INFO - Device Address"
    id: infodeviceaddress
  - platform: template
    name: "INFO - Manufacturer"
    id: infomanufacturer

The %s code for for sscanf stops at whitespace which includes spaces. The sample data looks like there are spaces right after the : so it isn’t returning any characters. Try "\rManufacturer : %31s" with a space after the : for the format string. This will break for any strings that include spaces; only returning the first word.

Also the char array tempManufacturer is the same as a string.c_str() so you could get rid of the extra assignment.

          char manufacturer[32];
          if (sscanf(str.c_str(), "\rManufacturer        : %31s", manufacturer) == 1) {
            id(infomanufacturer).publish_state(manufacturer); 
          }

As an alternative and avoiding issue with spaces, it looks like the value of the strings info items always starts in position 24 so std::string result = str.substr (24); So sscanf() wouldn’t need to be used. Use if (str.rfind("\rManufacturer", 0) == 0) to see if the returned line starts with the desired value and then .substr() to return the result.