├── LICENSE ├── README.md ├── ROVER MODBUS (1).pdf └── renogy_rs232.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 wrybread 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32ArduinoRenogy 2 | 3 | This lets you read data from Renogy charge controllers via their RS232 port using an ESP32 or Arduino. 4 | 5 | ![Wanderer](https://sinkingsensation.com/stuff/renogy/wanderer.jpg) 6 | 7 | The inspiration came when I needed to power a pump at a remote pond whenever there was sufficient power from a solar panel, and I was surprised to learn that the load switch on the Renogy controllers can't do that natively. The load switch is only designed to control lights at night. Silly. This lets me use the load switch to power whatever I want, but see the "Notes on the Load Switch" below for some notes on its limitations. And of course it lets me do whatever else I want with the data from the Renogy charge controller including using a relay, and gives me a dirt cheap way to read the battery voltage, panel voltage and load wattage from an ESP32 or Arduino. 8 | 9 | So far I've only tested this with Renogy Wanderer 30A (CTRL-WND30-LI) and Wanderer 10A charge controllers, please post to the Issues section if you test on more and I'll add it to the list below. It *should* work with any Renogy charge controller that has an RS232 port, which I think is all of them since they want to sell their [bluetooth module](https://www.renogy.com/bt-2-bluetooth-module/) that works with the RS232 port. 10 | 11 | Here's a pic of my installation: 12 | 13 | ![installation](https://sinkingsensation.com/stuff/renogy/box.jpg) 14 | 15 | ## Making the Cable 16 | 17 | Here's an overall wiring diagram: 18 | 19 | ![wiring diagram](https://sinkingsensation.com/stuff/renogy/wiring.png) 20 | 21 | You'll need make a cable to connect the controller to your ESP32 or Arduino. Start with an RJ12 cable, which is an old phone cable with 6 wires. Make sure it doesn't have only 4 wires, which is an RJ11 cable. 22 | 23 | Here's the RJ12 jack on my cable, note how there's 6 wires (white, black, red, green, yellow, blue): 24 | 25 | ![rj12 cable jack](https://sinkingsensation.com/stuff/renogy/jack.jpg) 26 | 27 | I don't know how standard the wire colors are but those are the colors on mine. You need the first 3 wires from the left, which on mine are the white, black and red wire. 28 | 29 | You'll also need a TTL to RS232 level adjuster. I used [this one](https://www.amazon.com/dp/B07BJJ3TZR) but there are lots of other options (google "max3232"): 30 | 31 | ![level adjuster](https://sinkingsensation.com/stuff/renogy/converter.jpg) 32 | 33 | If the listing goes away, it's the "NOYITO TTL to RS232 Module TTL RS232 Mutual Conversion Module", costs $7. See the wiring diagram above for instructions on connecting to it. 34 | 35 | ## Use 36 | 37 | After connecting the cable and flashing your ESP or Arduino you should be seeing data from your charge controller in Serial Monitor: 38 | 39 | ![serial monitor](https://sinkingsensation.com/stuff/renogy/serial_monitor.jpg) 40 | 41 | Feel free to post to Issues if not. 42 | 43 | ## Notes on the Code 44 | 45 | There's a struct that holds all the data from the charge controller. A look at the struct shows what data that is: 46 | 47 | ``` 48 | struct Controller_data { 49 | 50 | uint8_t battery_soc; // percent 51 | float battery_voltage; // volts 52 | float battery_charging_amps; // amps 53 | uint8_t battery_temperature; // celcius 54 | uint8_t controller_temperature; // celcius 55 | float load_voltage; // volts 56 | float load_amps; // amps 57 | uint8_t load_watts; // watts 58 | float solar_panel_voltage; // volts 59 | float solar_panel_amps; // amps 60 | uint8_t solar_panel_watts; // watts 61 | float min_battery_voltage_today; // volts 62 | float max_battery_voltage_today; // volts 63 | float max_charging_amps_today; // amps 64 | float max_discharging_amps_today; // amps 65 | uint8_t max_charge_watts_today; // watts 66 | uint8_t max_discharge_watts_today; // watts 67 | uint8_t charge_amphours_today; // amp hours 68 | uint8_t discharge_amphours_today; // amp hours 69 | uint8_t charge_watthours_today; // watt hours 70 | uint8_t discharge_watthours_today; // watt hours 71 | uint8_t controller_uptime_days; // days 72 | uint8_t total_battery_overcharges; // count 73 | uint8_t total_battery_fullcharges; // count 74 | 75 | // convenience values 76 | float battery_temperatureF; // fahrenheit 77 | float controller_temperatureF; // fahrenheit 78 | float battery_charging_watts; // watts. necessary? Does it ever differ from solar_panel_watts? 79 | long last_update_time; // millis() of last update time 80 | }; 81 | Controller_data renogy_data; 82 | ``` 83 | 84 | For example there's battery_soc (battery state of charge), the battery_voltage, the battery_charging_amps, etc. There's examples in the code of how to access them, but for example you get the battery voltage like this: 85 | 86 | ``` 87 | Serial.println( renogy_data.battery_voltage ); 88 | ``` 89 | 90 | There's another struct that holds the info about the charge controller (it's voltage rating, amp rating, serial number, etc). I don't think all those are working yet. For example the serial number it reports is different than the one printed on my charge controller, which I think is just because of how my code processes the serial number data, but I don't need that functionality so I haven't fixed that yet. 91 | 92 | And note the commented out section in the code that turns the load switch on and off. For example this would turn the load on for 10 seconds: 93 | 94 | ``` 95 | renogy_control_load(1) 96 | delay(10000); 97 | renogy_control_load(0) 98 | ``` 99 | 100 | ## Notes on the Load Switch 101 | 102 | I think the load switch uses a MOSFET, which means anything with an inductive kickback (like a brush motor) might damage the switch or the unit itself. But I've been powering a bilge pump on it, which I think is an inductive load, for months without an issue. I'm willing to risk my cheapie PWM charge controller for that, but be careful what you power from the load switch directly. And of course you can connect a relay to the load switch to control anything else, or just connect a relay directly to your Arduino or ESP32. 103 | 104 | Renogy is oddly uncommunicative about the limits of the load switch, and my theory is that that's because it's a bit complicated. My theory is that the capacity is shared between the battery input and the switch output. So for example if the 10 amp charge controller is pulling 8 amps of power from the solar panels, that leaves only 2 amps for the load switch. That would explain both why they don't mention the actual load limit, and why they only let us control the load at night (when the solar panels aren't competing for the capacity). But still, if that's the case, it would be nice for Renogy to mention that. And furthermore having options to control the load switch in the day would allow for someone to either have a larger controller than their panel so daytime power wouldn't be an issue, or to just use a relay connected to the load switch. Furthermore the manual says the controller will shut off the load switch and show an error code if the capacity reaches 105%, so maybe exceeding the load limit by a bit isn't catastrophic. 105 | 106 | Anyway you can of course bypass all these issues by just controlling a relay directly from your ESP or Arduino. 107 | 108 | For the record here's a screenshot from the Renogy Wanderer 10a manual showing the modes of the load switch: 109 | 110 | ![load modes](https://sinkingsensation.com/stuff/renogy/load_modes.jpg) 111 | 112 | Such an odd limitation to design them only to handle lights. Oh well, nothing an ESP32 or Arduino can't fix. 113 | 114 | ## General Notes 115 | 116 | - as of now this is untested on an Arduino, I've only used it with ESP32's (both a Rover and a Wroom). It should work fine with an Arduino, the only thing I'm uncertain about is whether you can have two Serial interfaces with all Arduinos (and this script uses one Serial interface for communication with the Renogy controller and one to print status to the console). It should work fine though. If you test with an Arduino please post to this project's Issues section. 117 | 118 | - I couldn't power the ESP32 directly from the charge controller, my theory is that it's so low power that the charge controller thinks nothing is connected and shuts off the USB port. Oh well, I used one of [these](https://www.amazon.com/gp/product/B08H89LTP5) 12v to 5v adaptors: 119 | 120 | ![12v to 5v](https://sinkingsensation.com/stuff/renogy/12v_to_5v.jpg) 121 | 122 | ## See Also 123 | 124 | There's a couple of other projects that get data from the Renogy charge controllers. Oddly I couldn't find anything that uses an ESP32 or Arduino though. 125 | 126 | - [here's a NodeJS project](https://github.com/mickwheelz/NodeRenogy) 127 | 128 | - [here's a pure javascript project](https://github.com/menloparkinnovation/renogy-rover) 129 | 130 | Much thanks to them! They made this much easier. 131 | 132 | Those are all for Raspberry Pi's or similar though. For my use case a Raspberry Pi is vastly overkill and has a lot of drawbacks: it needs to be shutdown properly, has a corruptible SD card, uses about 10 times the power of an Arduino or ESP (and an ESP32 can use deep sleep mode, which uses almost no power at all), has a super slow bootup time as opposed to instantly on, has an OS which needs to be kept up to date and is subject to lock ups, etc. ESP32's are in general much more robust in my experience. Once you get it working it just works, I'm betting for decades. That's not to say there aren't times where a Pi or similar makes more sense of course, but a remote solar installation probably isn't one of them. 133 | 134 | I also attached Renogy's RS232/modbus manual, it's somewhat occaisonally helpful if you squint at it long enough. 135 | 136 | ## Tested On 137 | 138 | So far it's been tested and confirmed working well on these controllers: 139 | 140 | - Wanderer 30A (CTRL-WND30-LI) 141 | - Wanderer 10A 142 | 143 | It *should* work on any Renogy charge controller with an RS232 port. And most if not all of them have that port since Renogy wants to sell their [bluetooth module](https://www.renogy.com/bt-2-bluetooth-module/), which uses that port to get its data. This should also be pretty easy to adapt to other charge controllers that make their data available via modbus. 144 | 145 | Please post to the Issues section if you test on other Renogy charge controllers, or email me (wrybread@gmail.com). 146 | 147 | ## To Do 148 | 149 | - I don't think it's correctly reading the serial number register, and possibly some of the other registers having to do with the model number. Please let me know if you figure out any fixes. (wrybread@gmail.com or post to Issues). 150 | 151 | - would be nice to add Bluetooth support for monitoring from a phone, posting to MQTT brokers and pvwatts, etc. 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /ROVER MODBUS (1).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wrybread/ESP32ArduinoRenogy/2c8d4b1fc95da336a2effbf82029d11294015c40/ROVER MODBUS (1).pdf -------------------------------------------------------------------------------- /renogy_rs232.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Reads the data from the Renogy charge controller via it's RS232 port using an ESP32 or similar. Tested with Wanderer 30A (CTRL-WND30-LI) and Wanderer 10A 4 | 5 | See my Github repo for notes on building the cable: 6 | https://github.com/wrybread/ESP32ArduinoRenogy 7 | 8 | Notes: 9 | - I don't think can power the ESP32 from the Renogy's USB port.. Maybe it's so low power that it shuts off? 10 | 11 | 12 | To do: 13 | - find out how much of a load the load port can handle... 14 | - test with an Arduino 15 | 16 | 17 | */ 18 | 19 | // https://github.com/syvic/ModbusMaster 20 | #include 21 | ModbusMaster node; 22 | 23 | 24 | 25 | 26 | /* 27 | A note about which pins to use: 28 | - I was originally using pins 17 and 18 (aka RX2 and TX2 on some ESP32 devboards) for RX and TX, 29 | which worked on an ESP32 Wroom but not an ESP32 Rover. So I switched to pins 13 and 14, which works on both. 30 | I haven't tested on an Arduino board though. 31 | */ 32 | #define RXD2 13 33 | #define TXD2 14 34 | 35 | /* 36 | Number of registers to check. I think all Renogy controllers have 35 37 | data registers (not all of which are used) and 17 info registers. 38 | */ 39 | const uint32_t num_data_registers = 35; 40 | const uint32_t num_info_registers = 17; 41 | 42 | 43 | // if you don't have a charge controller to test with, can set this to true to get non 0 voltage readings 44 | bool simulator_mode = false; 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | // A struct to hold the controller data 54 | struct Controller_data { 55 | 56 | uint8_t battery_soc; // percent 57 | float battery_voltage; // volts 58 | float battery_charging_amps; // amps 59 | uint8_t battery_temperature; // celcius 60 | uint8_t controller_temperature; // celcius 61 | float load_voltage; // volts 62 | float load_amps; // amps 63 | uint8_t load_watts; // watts 64 | float solar_panel_voltage; // volts 65 | float solar_panel_amps; // amps 66 | uint8_t solar_panel_watts; // watts 67 | float min_battery_voltage_today; // volts 68 | float max_battery_voltage_today; // volts 69 | float max_charging_amps_today; // amps 70 | float max_discharging_amps_today; // amps 71 | uint8_t max_charge_watts_today; // watts 72 | uint8_t max_discharge_watts_today; // watts 73 | uint8_t charge_amphours_today; // amp hours 74 | uint8_t discharge_amphours_today; // amp hours 75 | uint8_t charge_watthours_today; // watt hours 76 | uint8_t discharge_watthours_today; // watt hours 77 | uint8_t controller_uptime_days; // days 78 | uint8_t total_battery_overcharges; // count 79 | uint8_t total_battery_fullcharges; // count 80 | 81 | // convenience values 82 | float battery_temperatureF; // fahrenheit 83 | float controller_temperatureF; // fahrenheit 84 | float battery_charging_watts; // watts. necessary? Does it ever differ from solar_panel_watts? 85 | long last_update_time; // millis() of last update time 86 | bool controller_connected; // bool if we successfully read data from the controller 87 | }; 88 | Controller_data renogy_data; 89 | 90 | 91 | // A struct to hold the controller info params 92 | struct Controller_info { 93 | 94 | uint8_t voltage_rating; // volts 95 | uint8_t amp_rating; // amps 96 | uint8_t discharge_amp_rating; // amps 97 | uint8_t type; 98 | uint8_t controller_name; 99 | char software_version[40]; 100 | char hardware_version[40]; 101 | char serial_number[40]; 102 | uint8_t modbus_address; 103 | 104 | float wattage_rating; 105 | long last_update_time; // millis() of last update time 106 | }; 107 | Controller_info renogy_info; 108 | 109 | 110 | 111 | 112 | 113 | 114 | void setup() 115 | { 116 | Serial.begin(115200); 117 | Serial.println("Started!"); 118 | 119 | // create a second serial interface for modbus 120 | Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); 121 | 122 | // my Renogy Wanderer has an (slave) address of 255! Not in docs??? 123 | // Do all Renogy charge controllers use this address? 124 | int modbus_address = 255; 125 | node.begin(modbus_address, Serial2); 126 | } 127 | 128 | 129 | void loop() 130 | { 131 | static uint32_t i; 132 | i++; 133 | 134 | // set word 0 of TX buffer to least-significant word of counter (bits 15..0) 135 | node.setTransmitBuffer(0, lowWord(i)); 136 | // set word 1 of TX buffer to most-significant word of counter (bits 31..16) 137 | node.setTransmitBuffer(1, highWord(i)); 138 | 139 | renogy_read_data_registers(); 140 | renogy_read_info_registers(); 141 | 142 | Serial.println("Battery voltage: " + String(renogy_data.battery_voltage)); 143 | Serial.println("Battery charge level: " + String(renogy_data.battery_soc) + "%"); 144 | Serial.println("Panel wattage: " + String(renogy_data.solar_panel_watts)); 145 | Serial.println("controller_temperatureF=" + String(renogy_data.controller_temperatureF)); 146 | Serial.println("battery_temperatureF=" + String(renogy_data.battery_temperatureF)); 147 | Serial.println("---"); 148 | 149 | 150 | // turn the load on for 10 seconds 151 | //renogy_control_load(1) 152 | //delay(10000); 153 | //renogy_control_load(0) 154 | 155 | 156 | delay(1000); 157 | 158 | } 159 | 160 | 161 | 162 | void renogy_read_data_registers() 163 | { 164 | uint8_t j, result; 165 | uint16_t data_registers[num_data_registers]; 166 | char buffer1[40], buffer2[40]; 167 | uint8_t raw_data; 168 | 169 | // prints data about each read to the console 170 | bool print_data=0; 171 | 172 | result = node.readHoldingRegisters(0x100, num_data_registers); 173 | if (result == node.ku8MBSuccess) 174 | { 175 | if (print_data) Serial.println("Successfully read the data registers!"); 176 | renogy_data.controller_connected = true; 177 | for (j = 0; j < num_data_registers; j++) 178 | { 179 | data_registers[j] = node.getResponseBuffer(j); 180 | if (print_data) Serial.println(data_registers[j]); 181 | } 182 | 183 | renogy_data.battery_soc = data_registers[0]; 184 | renogy_data.battery_voltage = data_registers[1] * .1; // will it crash if data_registers[1] doesn't exist? 185 | renogy_data.battery_charging_amps = data_registers[2] * .1; 186 | 187 | renogy_data.battery_charging_watts = renogy_data.battery_voltage * renogy_data.battery_charging_amps; 188 | 189 | //0x103 returns two bytes, one for battery and one for controller temp in c 190 | uint16_t raw_data = data_registers[3]; // eg 5913 191 | renogy_data.controller_temperature = raw_data/256; 192 | renogy_data.battery_temperature = raw_data%256; 193 | // for convenience, fahrenheit versions of the temperatures 194 | renogy_data.controller_temperatureF = (renogy_data.controller_temperature * 1.8)+32; 195 | renogy_data.battery_temperatureF = (renogy_data.battery_temperature * 1.8)+32; 196 | 197 | renogy_data.load_voltage = data_registers[4] * .1; 198 | renogy_data.load_amps = data_registers[5] * .01; 199 | renogy_data.load_watts = data_registers[6]; 200 | renogy_data.solar_panel_voltage = data_registers[7] * .1; 201 | renogy_data.solar_panel_amps = data_registers[8] * .01; 202 | renogy_data.solar_panel_watts = data_registers[9]; 203 | //Register 0x10A - Turn on load, write register, unsupported in wanderer - 10 204 | renogy_data.min_battery_voltage_today = data_registers[11] * .1; 205 | renogy_data.max_battery_voltage_today = data_registers[12] * .1; 206 | renogy_data.max_charging_amps_today = data_registers[13] * .01; 207 | renogy_data.max_discharging_amps_today = data_registers[14] * .1; 208 | renogy_data.max_charge_watts_today = data_registers[15]; 209 | renogy_data.max_discharge_watts_today = data_registers[16]; 210 | renogy_data.charge_amphours_today = data_registers[17]; 211 | renogy_data.discharge_amphours_today = data_registers[18]; 212 | renogy_data.charge_watthours_today = data_registers[19]; 213 | renogy_data.discharge_watthours_today = data_registers[20]; 214 | renogy_data.controller_uptime_days = data_registers[21]; 215 | renogy_data.total_battery_overcharges = data_registers[22]; 216 | renogy_data.total_battery_fullcharges = data_registers[23]; 217 | renogy_data.last_update_time = millis(); 218 | 219 | // Add these registers: 220 | //Registers 0x118 to 0x119- Total Charging Amp-Hours - 24/25 221 | //Registers 0x11A to 0x11B- Total Discharging Amp-Hours - 26/27 222 | //Registers 0x11C to 0x11D- Total Cumulative power generation (kWH) - 28/29 223 | //Registers 0x11E to 0x11F- Total Cumulative power consumption (kWH) - 30/31 224 | //Register 0x120 - Load Status, Load Brightness, Charging State - 32 225 | //Registers 0x121 to 0x122 - Controller fault codes - 33/34 226 | 227 | if (print_data) Serial.println("---"); 228 | } 229 | else 230 | { 231 | if (result == 0xE2) 232 | { 233 | Serial.println("Timed out reading the data registers!"); 234 | } 235 | else 236 | { 237 | Serial.print("Failed to read the data registers... "); 238 | Serial.println(result, HEX); // E2 is timeout 239 | } 240 | // Reset some values if we don't get a reading 241 | renogy_data.controller_connected = false; 242 | renogy_data.battery_voltage = 0; 243 | renogy_data.battery_charging_amps = 0; 244 | renogy_data.battery_soc = 0; 245 | renogy_data.battery_charging_amps = 0; 246 | renogy_data.controller_temperature = 0; 247 | renogy_data.battery_temperature = 0; 248 | renogy_data.solar_panel_amps = 0; 249 | renogy_data.solar_panel_watts = 0; 250 | renogy_data.battery_charging_watts = 0; 251 | if (simulator_mode) { 252 | renogy_data.battery_voltage = 13.99; 253 | renogy_data.battery_soc = 55; 254 | } 255 | } 256 | 257 | 258 | } 259 | 260 | 261 | void renogy_read_info_registers() 262 | { 263 | uint8_t j, result; 264 | uint16_t info_registers[num_info_registers]; 265 | char buffer1[40], buffer2[40]; 266 | uint8_t raw_data; 267 | 268 | // prints data about the read to the console 269 | bool print_data=0; 270 | 271 | result = node.readHoldingRegisters(0x00A, num_info_registers); 272 | if (result == node.ku8MBSuccess) 273 | { 274 | if (print_data) Serial.println("Successfully read the info registers!"); 275 | for (j = 0; j < num_info_registers; j++) 276 | { 277 | info_registers[j] = node.getResponseBuffer(j); 278 | if (print_data) Serial.println(info_registers[j]); 279 | } 280 | 281 | // read and process each value 282 | //Register 0x0A - Controller voltage and Current Rating - 0 283 | // Not sure if this is correct. I get the correct amp rating for my Wanderer 30 (30 amps), but I get a voltage rating of 0 (should be 12v) 284 | raw_data = info_registers[0]; 285 | renogy_info.voltage_rating = raw_data/256; 286 | renogy_info.amp_rating = raw_data%256; 287 | renogy_info.wattage_rating = renogy_info.voltage_rating * renogy_info.amp_rating; 288 | //Serial.println("raw ratings = " + String(raw_data)); 289 | //Serial.println("Voltage rating: " + String(renogy_info.voltage_rating)); 290 | //Serial.println("amp rating: " + String(renogy_info.amp_rating)); 291 | 292 | 293 | //Register 0x0B - Controller discharge current and type - 1 294 | raw_data = info_registers[1]; 295 | renogy_info.discharge_amp_rating = raw_data/256; // not sure if this should be /256 or /100 296 | renogy_info.type = raw_data%256; // not sure if this should be /256 or /100 297 | 298 | //Registers 0x0C to 0x13 - Product Model String - 2-9 299 | // Here's how the nodeJS project handled this: 300 | /* 301 | let modelString = ''; 302 | for (let i = 0; i <= 7; i++) { 303 | rawData[i+2].toString(16).match(/.{1,2}/g).forEach( x => { 304 | modelString += String.fromCharCode(parseInt(x, 16)); 305 | }); 306 | } 307 | this.controllerModel = modelString.replace(' ',''); 308 | */ 309 | 310 | //Registers 0x014 to 0x015 - Software Version - 10-11 311 | itoa(info_registers[10],buffer1,10); 312 | itoa(info_registers[11],buffer2,10); 313 | strcat(buffer1, buffer2); // should put a divider between the two strings? 314 | strcpy(renogy_info.software_version, buffer1); 315 | //Serial.println("Software version: " + String(renogy_info.software_version)); 316 | 317 | //Registers 0x016 to 0x017 - Hardware Version - 12-13 318 | itoa(info_registers[12],buffer1,10); 319 | itoa(info_registers[13],buffer2,10); 320 | strcat(buffer1, buffer2); // should put a divider between the two strings? 321 | strcpy(renogy_info.hardware_version, buffer1); 322 | //Serial.println("Hardware version: " + String(renogy_info.hardware_version)); 323 | 324 | //Registers 0x018 to 0x019 - Product Serial Number - 14-15 325 | // I don't think this is correct... Doesn't match serial number printed on my controller 326 | itoa(info_registers[14],buffer1,10); 327 | itoa(info_registers[15],buffer2,10); 328 | strcat(buffer1, buffer2); // should put a divider between the two strings? 329 | strcpy(renogy_info.serial_number, buffer1); 330 | //Serial.println("Serial number: " + String(renogy_info.serial_number)); // (I don't think this is correct) 331 | 332 | renogy_info.modbus_address = info_registers[16]; 333 | renogy_info.last_update_time = millis(); 334 | 335 | if (print_data) Serial.println("---"); 336 | } 337 | else 338 | { 339 | if (result == 0xE2) 340 | { 341 | Serial.println("Timed out reading the info registers!"); 342 | } 343 | else 344 | { 345 | Serial.print("Failed to read the info registers... "); 346 | Serial.println(result, HEX); // E2 is timeout 347 | } 348 | // anything else to do if we fail to read the info reisters? 349 | } 350 | } 351 | 352 | 353 | // control the load pins on Renogy charge controllers that have them 354 | void renogy_control_load(bool state) { 355 | if (state==1) node.writeSingleRegister(0x010A, 1); // turn on load 356 | else node.writeSingleRegister(0x010A, 0); // turn off load 357 | } 358 | --------------------------------------------------------------------------------