Shop OBEX P1 Docs P2 Docs Learn Events
Entering the EV world — Parallax Forums

Entering the EV world

First think I notice was driving past a gas station looking at the gas prices only to realize it doesn't matter.

I am used to pulling up to the gas pump and seeing how much gas and what it cost me. Now I plug it into an outlet at my house and have no idea what I just did. There is no pump fuel gauge or cost to tell me what just happened.

There are devices that can be installed in the power panel that will monitor everything that goes on in your house. Emporia is one of these devices.

I was thing of DIYing it with building a ferrite core and cutting it in half and monitoring with a hall effect sensor. This seems a little much and now looking at a different solution.


This takes some of the DIY out of the project and make a simple device that outputs 40ma per amp of power.

Now I need to build a program that converts the voltage into amps and displays it or maybe sends the data to my network for processing. I would need to monitor how many amps per hour are used to come up with a cost to charge an EV as we pay for power by the kilowatt hour.

I was thinking that a P2 could easily do this by using the analog feature of the pin with a resistor function added across as the circuit needs an added resistance to measure the voltage.

The only thing is how to measure time.

Comments

  • This is a simple current transformer? Ok, this is better than the linear hall effect sensor as it doesn't have offset and temperature drift problems. But you should also measure the voltage, say every milli second, and then multiply current times voltage to get power (kW) and then integrate that over time to get energy (kWh). This way you measure true RMS current. Only measuring (average) current will produce errors when there is reactive current, e.g. the power factor is not 1.0.

  • iseriesiseries Posts: 1,522

    The unit I am looking at is CTS-CS-CAX-10-050. This unit require 5 volts to power it and returns a voltage based on current flow. Not sure how this works.


    If the P2 is running at 200Mhz and I setup a loop to read every millisecond how much drift would there be in an hour?

  • ManAtWorkManAtWork Posts: 2,271
    edited 2026-01-13 11:37

    This sensor has an offset drift of +/-5mV. This sounds not too bad but means +/-0.5A. If you charge your EV say it's 16A @ 400V for 2 hours or 22kWh. But an offset error of 0.5A is 345W so during the rest of the day (22h) this could sum up to an error of 7.5kWh.

    Edit: never mind. The actual error is much lower if you do that true RMS calculation a DC error means a power draw for one half period but a feed-back for the other half.

  • iseriesiseries Posts: 1,522

    I setup a test program just using a potentiometer to see what the smart pin will read out.

    This is the test program:

    #include <stdio.h>
    #include <propeller.h>
    #include <smartpins.h>
    
    
    #define POWER 20
    
    int main(int argc, char** argv)
    {
        int i;
    
        printf("Starting\n");
    
        _pinstart(POWER, P_ADC | P_ADC_1X, 13, 0);
    
        while (1)
        {
            i = _rdpin(POWER);
            printf("Power: %d\n", i);
            _waitms(1000);
        }
    }
    

    This is a table of the results. The output reading jumps around so did not get a stable value to use.

    Will have to average the numbers somehow or maybe just divide by 10.

    Right now, the unit charges the car at 40 amp at 240 volts or about 9.6Kw per hour and can fully charge the car in under 10 hours.
    Based on the owner's manual they recommend only charging the unit to 80% to preserve the life of the batteries. Only charge to 100% on long trips.

  • iseriesiseries Posts: 1,522

    Never realized how complicated EV charging station are. While there are only two types of plugs, an AC plug and a DC plug the number of different cars is many.

    When you plug your car in the car talks to the charging station and negates what it wants. Some car run at 400 Volts while other can be up to 1,000 Volts.

    If the charging station only supplies 400 volts the car uses a buck convert to jump the voltage up to what it needs. It even uses one winding on the BLDC motor as part of the buck converter.

  • The P2 pin will add at least +- 1mV drift with the best code. Accurate DC measurement with the P2 pins is tricky.

    The sensor you linked looks fine. I believe it will give AC output with a 2.5V DC bias.

    Have a look at OpenEVSE. That unit uses a current transformer which is read by an ATmega328.
    https://github.com/OpenEVSE/OpenEVSE_PLUS/blob/master/OpenEVSE_PLUS_v5.5/OpenEVSE_PLUS_v5.5.pdf It works fairly well. I assume there is a high pass filter in the software to eliminate DC offset.

    There is a good chance that the AC current is reported over the CAN bus. Every vehicle is different.

    For timing you'll want to use a timer. The documentation has this example:

            GETCT   x               'get initial CT
            ADDCT1  x,#500          'make initial CT1 target
    
     .loop  WAITCT1                 'wait for CT to pass CT1 target
            ADDCT1  x,#500          'update CT1 target
            DRVNOT  #0              'toggle P0
            JMP     #.loop          'loop to the WAITCT1
    
    

    This can be done in high level languages too. The code here will run every 500 clock cycles regardless of how long the code inside takes. Using a delay function won't work because it won't compensate for the time it takes to run the actual program. Any crystal oscillator should have enough accuracy for this purpose. If you want to sample every 1mS (I would run faster, like 0.1mS) then the value would be 200000000/1000 = 200000 clock cycles.

    Or you could use the ADC smart pin for timing. If you set a sample period of 20000 then waiting for IN to go high will have the same effect.

  • evanhevanh Posts: 17,043

    @iseries said:
    ... If the charging station only supplies 400 volts the car uses a buck convert to jump the voltage up to what it needs.

    Now you're getting a taste as to why AC was chosen over DC in the early days of building out electricity distribution. Having just a plain ruggedly built transformer for handling this conversion eliminates so many points of failure.

  • iseriesiseries Posts: 1,522

    My current plan right now is to read the sensor every second and sum those values into minutes where I will divide it by 60 to the average voltage.

    Convert that value to amps and add that value to hours. Then by multiplying that value by 240 to get watts per hour.

    I figure I will need three bucks, Seconds, Minutes, and Hours.

    Mike

  • iseriesiseries Posts: 1,522

    Change in plans, decided that I could use the raw number coming from the smartpin. With 32-bit number I would not overflow for a couple of days, and I am just looking at a few hours. Conversion to watts though required a little careful math as to not overflow the numbers.

    First, I normalize the smartpin reading by subtracting 2666. Then take 60 reading each second to get a minute's worth of numbers. Drop those numbers into an hour and days.

    Since I wanted a rolling number of watts just like at the gas pump, I just needed to convert the total reading each second that was being recorded.

    Watts = SmartpinTotal / 3247 * 60 / 36
    

    Dividing by 3247 get me the actual voltage reading of the smartpin. To get to watts I would divide by .04 or in my case multiple by 25 and the use 240 volts but remove a zero to get 60 and then divide by 3600 removing two zeros to convert from seconds to hours.

    I thought about using an eInk display but found the constant update caused a lot of flashing and then thought about using a Nextion display but decided that was over kill for this project.

    I decided on the MAX7219 display which actual makes it look like a gas pump as the number change as you refuel the car.
    MAX7219
    The display will show the watts used as time goes by.

    #include <stdio.h>
    #include <propeller.h>
    #include <smartpins.h>
    #include "MAX7219.h"
    
    
    void average(void *);
    
    #define POWER 20
    #define DOUT 50
    #define LODA 51
    #define CLK  52
    
    
    int Wait;
    volatile int Minutes, Minute;
    volatile int Hours, Hour;
    volatile int Day;
    volatile int TCurrent, Current;
    volatile int Mins;
    int Stack[2046];
    char Buffer[1024];
    
    
    int main(int argc, char** argv)
    {
        int C;
        int i;
        int Power;
        int m;
        int Watts;
    
        printf("Starting\n");
    
        MAX_Init(DOUT, CLK, LODA, 8);
    
        cogstart(average, NULL, Stack, 2046);
    
        _waitms(1000);
    
        while (1)
        {
            Power = Current * 1000 / 12988;
            /* 3247 conversion factor to volts */
            /* 60 conversion factor to watts 25(.04) X 240 volts */
            /* 36 number of seconds in an hour 3600 */
            m = TCurrent / 3247 * 60 / 36;
            Watts = Power * 24;
    
            MAX_Number(m, 0);
    
            //printf("Mins: %d, Amps*10: %d, Watts: %d, TWatts: %d\n", Mins, Power, Watts, m);
            _waitms(1000);
        }
    }
    
    
    void average(void *par)
    {
        int i;
        int d;
        int hrs, mins;
        int mils;
    
        TCurrent = 0;
        Minutes = 0;
        Hours = 0;
    
        _pinstart(POWER, P_ADC | P_ADC_1X | P_LOW_15K, 0x0d, 0); // enable ADC with 15K resistance
    
        _waitms(500);
    
        hrs = 0;
        Mins = 0;
        while (1)
        {
            mils = _getms();
            /* Get 60 Reading for a Minute */
            for (i=0;i<60;i++)
            {
                Current = _rdpin(POWER) - 2666;
                TCurrent += Current;
                _waitms(999);
            }
    
            /* Convert Current into Amps per minute */
            Minute = TCurrent;
            Minutes += Minute;
    
            /* Have full hour now amps per hour */
            if (Mins++ > 59)
            {
                Hour = Minutes;
                Hours = Hour;
                Mins = 0;
                hrs++;
            }
    
            /* Power for the Day */
            if (hrs > 23)
            {
                Day += Hours;
                Hours = 0;
                Minutes = 0;
                TCurrent = 0;
                hrs = 0;
            }
    
            Wait = _getms() - mils;
        }
    
    }
    

    As you can see from the picture, I don't have the clamp on device to complete the project. It's on its way.

    Mike

  • evanhevanh Posts: 17,043
    edited 2026-01-18 14:37

    The built in muldiv64() is your friend for preventing overflows. Naming in FlexC is _muldiv64(). I also made a version that rounds to nearest that I call muldiv65().

    And of course using floats is also an option.

Sign In or Register to comment.