Welcome to Tesla Motors Club
Discuss Tesla's Model S, Model 3, Model X, Model Y, Cybertruck, Roadster and More.
Register

Tesla Wall Connector load sharing protocol

This site may earn commission on affiliate links.
@CDragon
hahaha, I misspelled your name in the previous post.
As I don't see an option to edit, this is the 3rd post in a row ;-)
Something strange I noticed:
When I up the Amps through the webpage (I edited it to be able to change amps by 1A from 6-20A), when I for example change the amps from 10A to 13A, the 'available amps' first jump to 16A, before going down to 13A!
How is that possible?
In my case that causes the TWC to cut the charging as the Voltage dropped too much.
If you have a high enough voltage drop to cause the car to throttle back the charge rate, you really should fix that first. Depending on the cause, it can be dangerous to continue charging like that.
 
If you have a high enough voltage drop to cause the car to throttle back the charge rate, you really should fix that first. Depending on the cause, it can be dangerous to continue charging like that.
He is trying to do solar car charging off-grid and without batteries. It is inherently less stable than an off-grid setup buffered with batteries, or a grid-tied solar setup.
 
  • Love
  • Informative
Reactions: neroden and tga
He is trying to do solar car charging off-grid and without batteries. It is inherently less stable than an off-grid setup buffered with batteries, or a grid-tied solar setup.
Oh, I missed that. Ok, I'll change my answer:
You should try to find the cause of your voltage drop, and make sure you don't have a bad connection somewhere. If the drop is from a long wire run (with the heat distributed over the run) or a fussy inverter, that's less risky than concentrated at the terminal of an outlet.
 
Thanks for your replies :)

It is not a problem. We have been charging like this for half a year now. First on with a 'Granny' cable on a fixed 10A or 13A.
The voltage drop is caused by the fact that the amount of Amps are fixed and, as we didn't connect batteries a buffer but are directly doing PV -> EV, the moment the total solar power in W is going down because of a cloud or so, the voltage will drop.

This is where the TWC is coming in. 2 reasons:
1) We can up the charging power as the TWC supports up to 3 fase 32A, 22kW, so we can expand our solar in the future if we want.
2) @CDragon wrote this nice script.
I want to rewrite the green energy part of the script to catch the Voltage doing down, and adjust the Amps sent to the car so the Voltage is always between 190V and 240V.

I am not so experienced with Python, so if anybody could look with me how to do that, I would appreciate.
Now I am adjusting it by hand, using the webpage. When I see the sun getting less strong, I adjust the amps myself.
I will keep you updated about this nice project :)
 
  • Love
Reactions: neroden
I installed all the stuff and the script runs basically well, many thanks @CDragon for your hard work!

Nevertheless, the "trackgreenenergy" part crashes every day or two, prompting the following lines:

--------------------------

14:49:39: SHB 4112: 00 00.00/00.00A 0000 0000 M: 09 00.00/06.00A 0000 0000
{"Id":"cc8dcff0-92a9-4ef3-980a-736f5d5e6b91","Name":"Solarinput","Serial":XXXXXX,"DeviceEnergyType":1,"FamilyType":1,"ActivePower":-0.35355000000000003,"ActivePowerUnit":"kW","CounterReading":-513.953,"CounterReadingUnit":"kWh","CounterReadingT1":-513.953,"CounterReadingImport":-513.953,"SwitchOn":true,"Voltage":234.5,"VoltageL1":234.5,"Current":-1.512,"PowerFactor":0.998,"PowerFactorL1":0.998,"Temperature":16.95,"ValueDate":"2019-03-16T13:48:55.0197151Z"}
14:49:41: Solar generating 353W so limit car charging to:

6.47A + 0.00A = 6.47A. Charge when above 6A (minAmpsPerTWC).

Exception in thread Thread-1:

Traceback (most recent call last):

File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner

self.run()

File "/usr/lib/python3.5/threading.py", line 862, in run

self._target(*self._args, **self._kwargs)

File "/home/pi/TWC/TWCManager.py", line 1246, in background_tasks_thread

check_green_energy()

File "/home/pi/TWC/TWCManager.py", line 1296, in check_green_energy

parsedJson = json.loads(jsonStr)

File "/usr/lib/python3.5/json/__init__.py", line 319, in loads

return _default_decoder.decode(s)

File "/usr/lib/python3.5/json/decoder.py", line 339, in decode

obj, end = self.raw_decode(s, idx=_w(s, 0).end())

File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode

raise JSONDecodeError("Expecting value", s, err.value) from None

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

14:59:39: SHB 4112: 00 00.00/00.00A 0000 0000 M: 09 00.00/06.00A 0000 0000
15:02:59: Web query: 'b'getStatus'', id 1027, time 1552744979, type 2
15:09:40: SHB 4112: 00 00.00/00.00A 0000 0000 M: 09 00.00/06.00A 0000 0000
15:19:41: SHB 4112: 00 00.00/00.00A 0000 0000 M: 09 00.00/06.00A 0000 0000
15:20:39: Web query: 'b'getStatus'', id 41052, time 1552746039, type 2
15:29:42: SHB 4112: 00 00.00/00.00A 0000 0000 M: 09 00.00/06.00A 0000 0000

---------------------------

So at time 14:49:39 was the last successful interrogation of my smart-meter, and then an error encountered. As I'm no IT-crack at all, I'd like to ask you guys out there if you have any clue what's the issue there??
Once this occured, the scipt keeps basically running, but without the "trackgreenenergy" part. It is not recommiting without restarting the script.

Cheers Chris :)
 
Last edited:
I received my HPWC last week and I also have my RS485<>USB converters in.

I started work on porting the Perl code to Python: GitHub - wido/smarthpwc: Smart (Tesla) charging with a EU HPWC in combination with a Dutch Smart Meter

It will be a Python library you can easily import and extend into your own work. I'll write a simple daemon with a HTTP API where you can query it and such, but underneath it will use the Python library.

Might publish it to Pypi when it all works fine.

Is the smarttwc working?
Could the load balancing function be integrated in TWCManager?
Would be nice if the load balancing would work with the dutch smart meter (DSMR) and with a AC current sensor shield.
RPIZ CT3T1 - lechacal
 
Is the smarttwc working?
Could the load balancing function be integrated in TWCManager?
Would be nice if the load balancing would work with the dutch smart meter (DSMR) and with a AC current sensor shield.
RPIZ CT3T1 - lechacal

Exactly what I'm looking for as well. As I posted earlier, you can use DSMReader to read your smart reader. That software also has an API, so that could be called by the "SmartTwc or TWCManager:

Hi, interesting project, thanks for the lots of work that has been done!

I have a slightly different use case compared to using green energy: I'm looking to use this to smart load balance my TWC with my house-usage. So basically limiting the maximum amount of current to the TWC if other equipment in my house needs the energy, thus protecting blowing the main fuses of the house.

I already have a script running on a Raspberry Pi that reads the live power consumption of my house with a DSMR-compatible meter. This script also has an API to read the actual live consumption. So what I guess I have to do, is hook this someway into the check_green_energy routine.

Now I'm not a programmer (but can do some tweaking), so question is: has somebody done this already, or has a more simple solution for this running?

Another question is: I also have a Rasberry Pi reading a Modbus-compatible energy meter measuring the power to the wall charger, so I can have real-time/historical reporting on the usage of the charging of the car. This also uses a RS-485 bus.
As the TWC is also using RS-485, would it be possible (is the TWC also using Modbus?) and safe to daisy chain this to the same bus-wiring?
 
You could try something like this to limit the main fuse current with the dutch smart meter.
DSRM-reader for RaspberryPi has the following RESTful API
documented here: API Documentation — DSMR-reader documentation
Requirements:
- Hardware: RaspberryPi 3
- OS: Raspbian OS
- Python: 3.5+
- Database: PostgreSQL 9+
- SD disk space: 1+ GB
- P1 telegram cable

I have not tried the the code!

Code:
# add the following global variable at the beginning

# set maxAmpsMains to 90% of the main power connection fuse of your house
# the most common main fuse values in the Netherlands are:
#   single phase 35amps = 28
#   or 3 phase 25amps = 22
maxAmpsMains = 22
    
    
 
            
# insert the following part after this line
# maxAmpsToDivideAmongSlaves = wiringMaxAmpsAllTWCs
            
    global maxAmpsMains
        
        
        # Request power with DSRM-reader API:
        import requests
        import json

        response = requests.get(
            'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1',
            headers={'X-AUTHKEY': 'YOUR-API-KEY'},
        )
        
        if response and response.status_code == 200:
            json_data = response.json()
            if json_data and 'results' in json_data:
                if 'phase_currently_delivered_l1' in json_data['results']:
                   mains[0] = results.get('phase_currently_delivered_l1')*1000/230
                   mains[1] = results.get('phase_currently_delivered_l2')*1000/230
                   mains[2] = results.get('phase_currently_delivered_l3')*1000/230
        #          phase_currently_delivered_l... (float) - Current electricity used by phase L1 (in kW)
        
        else:
            if debugLevel >= 1:
                print('DSRM-reader Error: {}'.format(response.text))
        
        
        
        # Define how many samples are taken to calculate an average
        # A small current spike should not trigger the main fuse.
        # 1,1 x I for one hour // 1,5 x I for 10 min // 2 x I for 1 min // 3 x I for 10s // 10 x I for 0.1s
        mainsSampleCount = 4
        
        # shift samples array to the right
        for i in range(mainsSampleCount -1, -1, -1):   
            mainsSample[i] = mainsSample[i-1]
        
        # find phase with highest current which is the limit for all phases
        mainsSample[0] = max(mains)
            
        # calculate average of the samples
        mainsAvg = sum(mainsSample) / len(mainsSample)
        
        
        if(debugLevel >= 1):
            print(time_now() +
                " mains[0] " + str(mains[0]) +
                " mains[1] " + str(mains[1]) +
                " mains[2] " + str(mains[2]) +
                " mainsSample[0] " + str(mainsSample[0]) +
                " mainsAvg " + str(mainsAvg)

        # calculate left over amps for all TWCs
        loadBalancingAmpsAllTWCs = maxAmpsMains - mainsAvg
            
        if(maxAmpsToDivideAmongSlaves > loadBalancingAmpsAllTWCs):
            # Never tell the slaves to draw more amps than the main fuse can handle.
            if(debugLevel >= 1):
                print(time_now() +
                    " maxAmpsToDivideAmongSlaves " + str(maxAmpsToDivideAmongSlaves) +
                    " limited by loadBalancingAmpsAllTWCs " + str(loadBalancingAmpsAllTWCs))
            maxAmpsToDivideAmongSlaves = loadBalancingAmpsAllTWCs
 
You could try something like this to limit the main fuse current with the dutch smart meter.
DSRM-reader for RaspberryPi has the following RESTful API
documented here: API Documentation — DSMR-reader documentation
Requirements:
- Hardware: RaspberryPi 3
- OS: Raspbian OS
- Python: 3.5+
- Database: PostgreSQL 9+
- SD disk space: 1+ GB
- P1 telegram cable

I have not tried the the code!

Code:
# add the following global variable at the beginning

# set maxAmpsMains to 90% of the main power connection fuse of your house
# the most common main fuse values in the Netherlands are:
#   single phase 35amps = 28
#   or 3 phase 25amps = 22
maxAmpsMains = 22
   
   
 
           
# insert the following part after this line
# maxAmpsToDivideAmongSlaves = wiringMaxAmpsAllTWCs
           
    global maxAmpsMains
       
       
        # Request power with DSRM-reader API:
        import requests
        import json

        response = requests.get(
            'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1',
            headers={'X-AUTHKEY': 'YOUR-API-KEY'},
        )
       
        if response and response.status_code == 200:
            json_data = response.json()
            if json_data and 'results' in json_data:
                if 'phase_currently_delivered_l1' in json_data['results']:
                   mains[0] = results.get('phase_currently_delivered_l1')*1000/230
                   mains[1] = results.get('phase_currently_delivered_l2')*1000/230
                   mains[2] = results.get('phase_currently_delivered_l3')*1000/230
        #          phase_currently_delivered_l... (float) - Current electricity used by phase L1 (in kW)
       
        else:
            if debugLevel >= 1:
                print('DSRM-reader Error: {}'.format(response.text))
       
       
       
        # Define how many samples are taken to calculate an average
        # A small current spike should not trigger the main fuse.
        # 1,1 x I for one hour // 1,5 x I for 10 min // 2 x I for 1 min // 3 x I for 10s // 10 x I for 0.1s
        mainsSampleCount = 4
       
        # shift samples array to the right
        for i in range(mainsSampleCount -1, -1, -1):  
            mainsSample[i] = mainsSample[i-1]
       
        # find phase with highest current which is the limit for all phases
        mainsSample[0] = max(mains)
           
        # calculate average of the samples
        mainsAvg = sum(mainsSample) / len(mainsSample)
       
       
        if(debugLevel >= 1):
            print(time_now() +
                " mains[0] " + str(mains[0]) +
                " mains[1] " + str(mains[1]) +
                " mains[2] " + str(mains[2]) +
                " mainsSample[0] " + str(mainsSample[0]) +
                " mainsAvg " + str(mainsAvg)

        # calculate left over amps for all TWCs
        loadBalancingAmpsAllTWCs = maxAmpsMains - mainsAvg
           
        if(maxAmpsToDivideAmongSlaves > loadBalancingAmpsAllTWCs):
            # Never tell the slaves to draw more amps than the main fuse can handle.
            if(debugLevel >= 1):
                print(time_now() +
                    " maxAmpsToDivideAmongSlaves " + str(maxAmpsToDivideAmongSlaves) +
                    " limited by loadBalancingAmpsAllTWCs " + str(loadBalancingAmpsAllTWCs))
            maxAmpsToDivideAmongSlaves = loadBalancingAmpsAllTWCs
Thanks! Impressive, looks to be exactly what I need.
Ary you also going to try to get this working?

I do not yet have TWCManager installed, but have DSMReader working with a P1 cable measuring the house usage.
I also have a USB-RS485 adaptor on the Raspberry Pi to read the MODBus of the electricity meter that measures the charge to the TWC so I can also monitor and log the power going to the car.

Theoretically I think it should be possible to daisy chain this RS-485 connection to the TWC as this is also RS-485 at the physical level, but I'm a bit worried that the MODBus and the TWC protocols may interfere or I may brake something in the TWC.

So I'll order another RS-485 adapter and see if I can get this working. I'll report back the progress here.

P.s. instead of building-in a RaspberryPiZero into the TWC, modifying the TWC to hook up to the 14V Dc and using Wifi, I have a Cat-6 cable connected to the RS-485 port in the TWC that runs into my house. There I can connect it to the RaspberryPi in my meter-cabinet that also measures the DSMR-meter (and later Solar energy) and have everything centralized. No need to modify the TWC and no WiFi problems, so this seems to me much simpeler. I'm wondering why nobody else seems to have this setup?
 
Lets assume the following situation:
  • Charge limit on car set to 80%
  • Car is charged by less than 80%
  • TWCManager is set to only charge with Green Energy, but nothing available to charge (e.g. no sun, at night)
Would the car ever go to sleep? I have the feeling that this is not the case as the car tries to query the TWC for power all the time.
In the end it looses a lot energy over night, cuz it stays up all night for nothing.
 
@CDragon, just got all the hardware to go for this project. I just want to thank you in advance for all your amazing and hard work. I think it's fair to say that you are actually contributing to a greener future, helping Tesla/solar system owners reduce reliance on fossil fuel as well as helping us reduce our utility bills. Thank you!

Like @Nietschy , I also have a Fronius-based system, Fronius Symo 5.0-3-S with an LG Chem RESU10H (Powerwalls don't support 3-phase and they're not yet available in Portugal) and am now developing the algorithm to charge the battery first, which in a sunny day happens in the morning until about 1130 AM. Then if the car is around and plugged, it would be its turn.

It'll be my first Python and API interfacing project, BTW, I haven't coded anything in quite a few years...
 
You could try something like this to limit the main fuse current with the dutch smart meter.
DSRM-reader for RaspberryPi has the following RESTful API
documented here: API Documentation — DSMR-reader documentation
Requirements:
- Hardware: RaspberryPi 3
- OS: Raspbian OS
- Python: 3.5+
- Database: PostgreSQL 9+
- SD disk space: 1+ GB
- P1 telegram cable

I have not tried the the code!

Code:
# add the following global variable at the beginning

# set maxAmpsMains to 90% of the main power connection fuse of your house
# the most common main fuse values in the Netherlands are:
#   single phase 35amps = 28
#   or 3 phase 25amps = 22
maxAmpsMains = 22
 
 
 
        
# insert the following part after this line
# maxAmpsToDivideAmongSlaves = wiringMaxAmpsAllTWCs
        
    global maxAmpsMains
    
    
        # Request power with DSRM-reader API:
        import requests
        import json

        response = requests.get(
            'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1',
            headers={'X-AUTHKEY': 'YOUR-API-KEY'},
        )
    
        if response and response.status_code == 200:
            json_data = response.json()
            if json_data and 'results' in json_data:
                if 'phase_currently_delivered_l1' in json_data['results']:
                   mains[0] = results.get('phase_currently_delivered_l1')*1000/230
                   mains[1] = results.get('phase_currently_delivered_l2')*1000/230
                   mains[2] = results.get('phase_currently_delivered_l3')*1000/230
        #          phase_currently_delivered_l... (float) - Current electricity used by phase L1 (in kW)
    
        else:
            if debugLevel >= 1:
                print('DSRM-reader Error: {}'.format(response.text))
    
    
    
        # Define how many samples are taken to calculate an average
        # A small current spike should not trigger the main fuse.
        # 1,1 x I for one hour // 1,5 x I for 10 min // 2 x I for 1 min // 3 x I for 10s // 10 x I for 0.1s
        mainsSampleCount = 4
    
        # shift samples array to the right
        for i in range(mainsSampleCount -1, -1, -1):
            mainsSample[i] = mainsSample[i-1]
    
        # find phase with highest current which is the limit for all phases
        mainsSample[0] = max(mains)
        
        # calculate average of the samples
        mainsAvg = sum(mainsSample) / len(mainsSample)
    
    
        if(debugLevel >= 1):
            print(time_now() +
                " mains[0] " + str(mains[0]) +
                " mains[1] " + str(mains[1]) +
                " mains[2] " + str(mains[2]) +
                " mainsSample[0] " + str(mainsSample[0]) +
                " mainsAvg " + str(mainsAvg)

        # calculate left over amps for all TWCs
        loadBalancingAmpsAllTWCs = maxAmpsMains - mainsAvg
        
        if(maxAmpsToDivideAmongSlaves > loadBalancingAmpsAllTWCs):
            # Never tell the slaves to draw more amps than the main fuse can handle.
            if(debugLevel >= 1):
                print(time_now() +
                    " maxAmpsToDivideAmongSlaves " + str(maxAmpsToDivideAmongSlaves) +
                    " limited by loadBalancingAmpsAllTWCs " + str(loadBalancingAmpsAllTWCs))
            maxAmpsToDivideAmongSlaves = loadBalancingAmpsAllTWCs
@minos
Thanks again for the head start with the code. I now have looked a little bit more at this code, and think I've found an issue:
the DSMR-reader reads the actual usage from the main house-meter, which means it measures all the power to both the house AND the charger.

So the difference between the current Amps measured by DSMR-reader and the MaxAmpsMains from the house is not the absolute Amps that can be advertised to the TWC (loadBalancingAmpsAllTWCs).

It is actually the delta of Amps that can be added to, or has to be extracted from, the current Amps going to the TWC's.
I'm puzzled how to solve this, as I believe the script does not know the current Amps going to the TWC.

Does this mean I also need to bring in the meter-readings from the separate energy meter for the TWC that I have installed? Or can this be solved by some sort of iterative approach?
 
You could try something like this to limit the main fuse current with the dutch smart meter.
DSRM-reader for RaspberryPi has the following RESTful API
documented here: API Documentation — DSMR-reader documentation
Requirements:
- Hardware: RaspberryPi 3
- OS: Raspbian OS
- Python: 3.5+
- Database: PostgreSQL 9+
- SD disk space: 1+ GB
- P1 telegram cable

I have not tried the the code!

Code:
# add the following global variable at the beginning

# set maxAmpsMains to 90% of the main power connection fuse of your house
# the most common main fuse values in the Netherlands are:
#   single phase 35amps = 28
#   or 3 phase 25amps = 22
maxAmpsMains = 22
  
  
 
          
# insert the following part after this line
# maxAmpsToDivideAmongSlaves = wiringMaxAmpsAllTWCs
          
    global maxAmpsMains
      
      
        # Request power with DSRM-reader API:
        import requests
        import json

        response = requests.get(
            'http://YOUR-DSMR-URL/api/v2/datalogger/dsmrreading?ordering=-timestamp&limit=1',
            headers={'X-AUTHKEY': 'YOUR-API-KEY'},
        )
      
        if response and response.status_code == 200:
            json_data = response.json()
            if json_data and 'results' in json_data:
                if 'phase_currently_delivered_l1' in json_data['results']:
                   mains[0] = results.get('phase_currently_delivered_l1')*1000/230
                   mains[1] = results.get('phase_currently_delivered_l2')*1000/230
                   mains[2] = results.get('phase_currently_delivered_l3')*1000/230
        #          phase_currently_delivered_l... (float) - Current electricity used by phase L1 (in kW)
      
        else:
            if debugLevel >= 1:
                print('DSRM-reader Error: {}'.format(response.text))
      
      
      
        # Define how many samples are taken to calculate an average
        # A small current spike should not trigger the main fuse.
        # 1,1 x I for one hour // 1,5 x I for 10 min // 2 x I for 1 min // 3 x I for 10s // 10 x I for 0.1s
        mainsSampleCount = 4
      
        # shift samples array to the right
        for i in range(mainsSampleCount -1, -1, -1): 
            mainsSample[i] = mainsSample[i-1]
      
        # find phase with highest current which is the limit for all phases
        mainsSample[0] = max(mains)
          
        # calculate average of the samples
        mainsAvg = sum(mainsSample) / len(mainsSample)
      
      
        if(debugLevel >= 1):
            print(time_now() +
                " mains[0] " + str(mains[0]) +
                " mains[1] " + str(mains[1]) +
                " mains[2] " + str(mains[2]) +
                " mainsSample[0] " + str(mainsSample[0]) +
                " mainsAvg " + str(mainsAvg)

        # calculate left over amps for all TWCs
        loadBalancingAmpsAllTWCs = maxAmpsMains - mainsAvg
          
        if(maxAmpsToDivideAmongSlaves > loadBalancingAmpsAllTWCs):
            # Never tell the slaves to draw more amps than the main fuse can handle.
            if(debugLevel >= 1):
                print(time_now() +
                    " maxAmpsToDivideAmongSlaves " + str(maxAmpsToDivideAmongSlaves) +
                    " limited by loadBalancingAmpsAllTWCs " + str(loadBalancingAmpsAllTWCs))
            maxAmpsToDivideAmongSlaves = loadBalancingAmpsAllTWCs
@minos: to bump this, topic, did you got this working yourself?
 
Hello I figured out how to get the PV performance from the Loxone Server. Works fine and all the data comes in.

Format:
<LL control="dev/sps/io/LeistungPV/state" value=„6400“ Code="200"/>

But I don’t know how to set up the python m = re.search Filter in the TWCManager.py config file. Just need to exclude the value“6400“.

TWCManager Example:
Solar,11/11/2017 14:20:43,-2.957,-0.29,124.3
m = re.search(b'^Solar,[^,]+,-?([^, ]+),', greenEnergyData, re.MULTILINE)

For Loxone it must be something like:
m = re.search(b'^value,[^,]+,[^, ]+),', greenEnergyData, re.MULTILINE)

Would be nice if theres someone with python skills.

Thanks a Lot!