Xantrex C35/C40/C60 Display

Replicating Xantrex's remote display with an Arduino, plus collecting more data!

Similar projects worth following
The Xantrex (also branded under Schneider and Trace) C series load/charge controllers are fairly simple, but robust PWM charge controllers. I bought a unit for my camper; only to recently finally use it during a power outage. In the midst of enjoying a cold fridge I decided I'd love to know just what the solar panels are outputting and keep track of the daily Amp-Hours.

Xantrex does offer a display, but at the cost of around $100, it's not very palatable. Data is great. Cheap data is awesome.


The display shows the following:

-Battery Voltage
-Input Voltage (wind, solar, etc) (averaged over the last ten readings)
-Amperage (averaged over the last ten readings)
-Wattage (averaged)
-Transistor Temperature and Duty Cycle (these two values alternate between each other)
-Amp-Hour counter

As far as features go:

-Automatic backlight control; if no input voltage for 60 seconds, backlight turns off
-Automatic reset of Amp-hour counter if an hour elapses with no input voltage

There might be enough overhead left to stream serial data to a PC, but I would recommend using a 16 mHz MCU. This code is written specifically for a 12 volt system with the charger in charge control mode. There are options for 24 and 48 volt settings which do change the scale of some of the voltage readings as well as other unknowns.


The schematics are fairly straightforward. The important bit is the wiring of the RJ25 plug:

  1. LED A
  2. Comm 1
  3. Comm 2
  4. Ground
  5. V+ (8 volts)
  6. Led B

Pardon the LCD, Fritzing doesn't have a serial LCD I could pick from. The purple lead is merely a serial Tx from the Arduino. The two LED leads are currently not being used, but otherwise are used to relocate the status LED that is normally found on the charger.

Source Code:

Ultimately the code is not as clean as I care for, but with the challenge of not using any interrupts it was necessary to break up functions and control when they could execute. This is done by using the time between bytes (which is longer than the time between bits) to perform calculations and stream data to the LCD. I tried to be healthy with the use of comments so hopefully it makes a little sense!

This is all written under the Arduino 1.0.6 IDE.

/*Digital Volt Meter for the Xantrex C-series charge/load controllers. Configured to read the obscure communication
protocol they use and display the information on a 2x16 Parallax LCD. Displays battery voltage, average amperage,
watts (calculated from average amps), input voltage, transistor temperature, duty cycle and total amp-hours. LCD
backlight is programmed to turn off after a period of no charging to conserve power and serve as a night mode, amp-hour
counter is also programmed to reset at night so amp-hour counter will perform as a daily counter, not total.*/

#include SoftwareSerial lcdSerial(11,12); //rx,tx configure softwareserial for LCD

//pin declarations
#define comm1pin 7
#define comm2pin 8
#define LED 13
#define LCDrx 11
#define LCDtx 12

//configurations declarations
#define noPowerTimeLimit 60000 //time in milliseconds in which display backlight turns off if not charging
#define noPowerAHTimeLimit 3600000 //time in milliseconds in which Amp-Hour counter resets (1 hour = 3600000)
#define yesPowerTimeLimit 5000 //time in milliseconds in which display backlight turns on if charging begins
#define avgAmpArraySize 10 //number of readings to keep in avgAmp (rolling average)
#define avgPvArraySize 10
#define commTimeout 5 //time in milliseconds both comm can be low before it's considered a timeout (time between bytes)

boolean tempOrDuty = true; //boolean for selecting whether temp or duty cycle is currently displayed
boolean commCheck = true; //boolean for latching communicatons logic, when true MCU will check for comm high
boolean resetTimeLimit = true;
boolean backLightOff = false; //status of whether backlight is enabled or not
boolean lastStateAmp = true;
byte syncCount = 0; //status as to whether a sync signal has been received, used to prevent writing to LCD until good data is present
byte temp = 0; //temp storage for byte in progress
byte comm1=0; //register for comm1 status
byte comm2=0; //register for comm2 status
byte count = 0; //count of number of bits stored in temp
byte byteCount = 0; //count of number of bytes collected since last sync
byte transTemp = 0; //transistor temp of charger
byte battTemp = 0; //battery temp
byte toggleCount = 0; //counter for how long to display temp or duty cycle; incremented by number of sync cycles
byte dutyCycle = 0;...
Read more »

  • 1 × Arduino Pro Mini 328: 3.3 V / 8MHz Sourced from Sparkfun
  • 1 × 2x16 Serial LCD This particular example sourced from Parallax
  • 1 × LM7805 Power Management ICs / Linear Voltage Regulators and LDOs
  • 2 × 0.01 uF Capacitor For filtering voltage into and out of the regulator
  • 2 × 35 uF Capacitor Further voltage filtering for the regulator

View all 9 components

  • Not done!

    Eric Herbers05/04/2015 at 03:46 0 comments

    After posting my findings on a post on allaboutcircuits it was pointed out that byte 1 really should be battery voltage. After a little investigation (and wiring in a battery temp sensor) I found that what I had been using (byte 13) was actually a comparator value for the charging logic that factored in battery temperature (causing the battery to charge to a lower voltage if it is hot).

    Ultimately I found that my logic for checking the two comm lines had a fault: It assumed both went high at exactly the same time. The fallacy in that logic is that any MCU takes a number of clock cycles between actions so it can't realistically bring both high at exactly the same time. Granted, the Arduino isn't checking them at exactly the same time either, but it was painfully obvious that I was not catching them both reliably.

    By writing in some logic that causes the Arduino to delay a millisecond and then check the other comm a second time ensures that it can reliably capture the sync events, leading me to reliable data for byte 0. Early on I had dismissed it as I was largely getting garbage data; that was a mistake.

    I've updated the source code to correct this and will work on a schematic next.

  • And done!

    Eric Herbers04/29/2015 at 04:15 0 comments

    I think I'll call it good at this point. Aside from intermittently getting out of sync the display is working to satisfaction and formatted in a fashion I'm happy with. Look to the project details for what the display does as well as source code.

  • Nothing Special

    Eric Herbers04/24/2015 at 16:55 0 comments

    Short update: Thanks to home projects I hadn't met my goal of completing this one, but progress has been made!

    I've decrypted enough information to at least get the core necessities; voltage, amperage and temperature. At this point I'm working on formatting information for the LCD, primarily trying to come up with icons so I can manage the limited space better.

    While an 8 mHz microcontroller is doing the job, 16 mHz would be so much better! Since I'm using softwareserial to talk to the LCD (so hardware serial can be left open for debugging) I was running into problems with not being able to keep the overhead low enough that it still caught every bit. Ultimately I had to increase complexity in the code a bit by making the MCU update only portions of calculations and the LCD at a time rather than doing everything at once. This seems to have resolved the problem.

    Had I not been using parts I had laying around, a 16 mHz MCU would have been my first choice.

    Next up will be focusing on features such as turning off the LCD backlight when there is no solar power to help conserve energy as well as decided how to handle the amp-hour counter; I've yet to decide if I should keep a life-time counter or have it reset at night and only keep track of daily counts.

  • Excelling in Excel

    Eric Herbers04/11/2015 at 05:05 0 comments

    Now that I was capturing reliable bytes of data, it was time to start decrypting what they meant. There are still a number of unknowns, but quite a few were identified simply by dumping the results in Excel and observing how the values changed. I also ended up hooking a car charger to the PV input on the charge controller and monitored an actual charge cycle while measuring voltages and changing the current from the car charger. This is tedious work with a fair amount of guessing, but it still pays off with much of the core necessary information identified.

    Ultimately the trick is to get the Arduino to stream the data out to the serial terminal, using commas to separate data and the print line function to start a new line of data. This can be copied and then pasted into Excel using the text import function quickly and easily. With everything sorted into columns and rows, you can use the scatter graph feature to plot out the data and get an idea of what is being viewed. By logging data while doing controlled actions with the charger you can break down a lot of the mysteries.

    Byte: Description (all conversion formulas are for the 12 volt operating mode)

    1: The starting byte of the string, signified by both comm lines going high on the first bit. Displays battery voltage: High resolution. Value dependent on voltage mode (12, 24 or 48). Slope of 0.0755, offset of +.464
    2: Current; divide by 2 to get real value.
    3: Always reads a value of 126, undetermined purpose.
    4: Appears to be related to wattage, but is inversed and not scaled linearly. Even applying a power doesn't yield accurate results. The results also don't match up perfectly to calculated wattage, but follows the trend.
    5: PV Voltage: Slope of 0.350, offset of +.032
    6: Battery Voltage: Low Resolution Scale: Conversion formula not determined yet
    7: Battery Temperature: Conversion formula not determined yet
    8: Almost always displays 174.
    9: Status of the reset button/load or charge control selection
    10: Bulk Charge Voltage Level: Slope of 0.0755, offset of +.464
    11: Float Charge Voltage Level: Slope of 0.0755, offset of +.464
    12: Transistor Temperature: Conversion formula not determined yet
    13: Temperature Compensated Battery Voltage Comparator, increases over actual battery voltage as battery temperature rises. This causes the battery to stop charging at a low voltage if temperature is high. Slope of 0.0755, offset of +.464
    14: Counter: Starts at 0, ends around 230, rolls over every minute
    15: Undetermined, might be a status register, but rarely strays from 0
    16: Undetermined, might be a status register, but rarely strays from 0
    17: Duty Cycle: 128 = 100%, 0 =0%
    18: Minutes on Float Charge, but only counts up to 23
    19: Minutes on Bulk Charge, but only counts up to 11
    20: Status Register: Yet to investigate
    21: Status Register: bit 8: Transistor Over-temp, 7: unknown, 6: Bulk Charge, 5-1: Unknown
    22: Status Register: Yet to investigate
    23: Can been seen as a reasonably repetitive pattern, but undetermined what it is. Counts up from 0 to around 45, but usually rolls over after only a few iterations

  • Bits and Bytes

    Eric Herbers04/08/2015 at 04:16 0 comments

    Hardware wise this project is very simple, nothing more than a serial LCD being driven by an Arduino Pro Mini. As all I'm doing is collecting data from a serial stream and formatting it onto a LCD, this could be done with much much less.

    At first blush the data appeared to be your typical RS-232 or RS-485. However, upon closer inspection it was obvious that a couple things were missing. Primarily a start or stop bit and no obvious formatting; just raw bytes. A plea was tossed out to twitter and caught a surprising amount of wind thanks to the Hackaday staff, but the protocol has still not been identified. By all intents and purposes it appears to be proprietary.

    The good news is that it's at a very slow rate of around 200-300 bits per second, meaning a lowly 8 mhz micro-controller can easily collect the data the simple way (using the digitalRead function and if statements). After beating my head against a wall to find a protocol, I finally said to hell with it and decided to just do it the hard way. Here's the important bits found after a few nights of testing and observation:

    -23 bytes of data
    -No start or stop bits; only 8 bit of data separated by a small delay
    -Two communication lines; complimentary data. 0 bits are signaled by pulling one line high and 1 bits by pulling the other line high
    -Both lines are pulled high at the same time to denote the start of a block of bytes; most of the time

    Ultimately here is what the data looks like:

    This being at a division of only 20 ms. The two comm lines are offset from one another to better identify the 1's and 0's.

    The code to read this is pretty simple:

    -Add a 1 or 0 to the byte in progress based on whether one or the other comm line is high.
    -If too much delay occurs between bits, clear out the register and begin anew. This is how the bytes are initially synced and kept in sync.
    -If both comm lines are high, go back to the start of the array of 23 bytes and begin repopulating it again. This is how we sync to the data so the correct bytes are associated with the correct variables.
    -Since the above only occurs about 98% of the time, also keep track of how many bytes have been stored and go back to the start of the array if it grows over 23.

    From there the array will get processed to produce varies figures such as voltage, duty cycle, amperage, temperature, etc. For now the raw data is just dump to a serial terminal while I try to decipher in Excel what everything is. So far I've got a loose understanding of about half the bytes (and at least those being the core necessary), but there is certainly more that are an unknown.

View all 5 project logs

Enjoy this project?



Victor J Chamorro wrote 03/20/2019 at 20:59 point


First, thanks for share this project, its awesome.
I'm build this project for my solar installation, but I have a Arduino ProMini 16Mhz 5v, not 8Mhz.

I removed the serialLCD and replace with Serial connection for watch the data in my PC.

The code need any changes? Any extra delay?

The return data is wrong, for example,  sometimes returns 6.5 V Battery and others 16.5 V.
My battery is 12v.


  Are you sure? yes | no

Todd wrote 07/04/2017 at 14:19 point

Thank you for sharing. I was able to get this up and operational. I am hoping to replace the LCD with Blynk and make it wireless. I'll tell you how it works when I am finished. 

  Are you sure? yes | no

johnphillips5946 wrote 12/20/2016 at 21:56 point

I am just in the process of putting a Xantrex C-40 into my home solar system, i really want to try building and implementing a clone of your project.  two questions did i see a picture of a spreadsheet of data you had collected from this device? if so is there documentation on what you did to collect that? and second, is your program available somewhere?

  Are you sure? yes | no

Stuart Longland wrote 07/04/2016 at 09:46 point

Silly question, how similar are these to the Xantrex TrueCharge series?  We have a couple of chargers here, one of which I'll probably use as the mains back-up charger for the #Solar-powered cloud computing cluster I'm building.  The part number for the remote head is given as 808-8040-00 in the handbook, I wonder if it's the same.

It'd be handy to read some of the telemetry off the charge controller directly.

(Ohh, and minor nit… 8mHz = 8×10¯³ Hz = 0.008 Hz, a very slow CPU indeed.  Good thing the AVR is fully static.)

  Are you sure? yes | no

Eric Herbers wrote 07/05/2016 at 14:45 point

Truth be told, I have no idea,  but I seriously doubt they're similar.

Considering how little overhead is required, at least in this case, a fast MCU is not necessary.

  Are you sure? yes | no

Vaxination wrote 07/01/2015 at 22:49 point

any idea how hard this would be to implement for a raspberry pi with the breadboard?

  Are you sure? yes | no

Eric Herbers wrote 07/02/2015 at 02:40 point

I'm afraid not, I have very little experience with the Raspberry Pi. Ultimately I've done ample to document what is necessary to read the data produced by the charger, but it'll be up to you to program it in a language that will work on the Raspberry Pi.

Do I think the Pi can do this? Absolutely.

  Are you sure? yes | no

Vaxination wrote 07/02/2015 at 18:22 point

thanks I enlisted another (smarter) buddy on this one, we are thinking the simple solution might be to just get an arduino and interface it with the pi, basically using the arduino to process it into something useful and then let the pi act as a server to farm out the data to a web interface (I'm less concerned about the LCD though that might be kind of nice too)

  Are you sure? yes | no

Eric Herbers wrote 07/02/2015 at 21:49 point

Yeah, at least the interface side of the work is largely done in that case; the code would only need minor editing to spit data to the Pi.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates