# Analyzing, troubleshooting, & fixing EMW 10kW DIY Charger



## PStechPaul (May 1, 2012)

I have finally gotten started on my efforts to work on a couple of these chargers that forum members have had trouble with, and sent to me to see what I might be able to do. I am still recuperating from cervical spine surgery so my mobility is a bit limited, but I have made some progress, and I will report here as I go.

Today, I removed the display and controller board, and connected it to the FTDI module, which supplies 5 VDC sufficient to light the display and it shows the default information for the uLCD-144-G2 module. But the red and green buttons do nothing. 

I will say that the control board was rather difficult to remove, because it is held in by four screws, four unthreaded spacers, and four locknuts, one of which was almost impossible to reach with long pliers. I also had to bend the front panel somewhat so the board could be removed, and one connector (12 VDC to the fans) came loose from the board and it is not polarized or keyed so it's not easy to figure out where it goes.

My next step was to try and use the FTDI module to read the program (sketch) already programmed in the Arduino Pro Mini. The basic Arduino IDE seems only able to erase and program a compiled sketch to the AtMega328P, although it must be able to read it in order to verify.

So I searched and found an application called AVRDude, but it is a command line interface which needs various parameters and it does not appear to support the FTDI module. I had purchased the AVR Dragon some time ago and I decided to use it. But first I had to go to www.atmel.com/avrdragon, and I had to install various support files as well as the Atmel Studio 6.2, which also required additional support files such as Visual Studio 10 and such, so now more than two hours later it is finally installed. But I still need to find out how to connect the Dragon to the Arduino...

More later.


----------



## PStechPaul (May 1, 2012)

Here is the Dragon:










These are the connections from the Dragon SPI programming interface to the ATMega328:










And here is the schematic of the Arduino Pro Mini:










So now I just need to make up a header to connect the Dragon to the Arduino, and I should be able to read and write the flash memory. I'm not sure if it will allow debug in ISP mode. There is apparently a "Debug Fuse" that needs to be programmed, and it can be done with the SPI connection. Then, debugging is done through the "DebugWire" interface, which uses only one signal wire (RESET) and the Vcc and GND:

http://www.atmel.com/webdoc/avrdragon/avrdragon.dw_description.html


----------



## AntronX (Feb 23, 2009)

Can you post complete schematic? Too lazy to plow through original thread.


----------



## PStechPaul (May 1, 2012)

Here is the basic power schematic:










The driver board:










Version 14:









And the control board V12: http://enginuitysystems.com/files/EMW/Control_Board_V12.pdf

And V14:


















Here is my initial concept of an overall schematic using some of the EMW design. I'll modify it so that it shows how the present V12-14 design is connected:


----------



## PStechPaul (May 1, 2012)

Here is the overall schematic of the EMW charger in PDF format:

http://enginuitysystems.com/files/EMW/EMW_PFC_Overall.pdf

Here are images (not very clear, but you can get the idea:



















I had to grit my teeth and try not to slather red ink on all the poor design choices and oddities, but this is mostly how it is actually built. There are probably some mistakes that I may have made trying to interpret the schematics, and I did not show some unused circuitry. Please have a close look and make any corrections as needed. 

[edit] I made some corrections, especially the Arduino Pro Mini.

[edit2] I can see a few more errors, which I will correct as I go and the files will update if you refresh the page. For one thing, the control circuitry is isolated from the power circuit, and now I see how that is done.


----------



## PStechPaul (May 1, 2012)

I made an adapter cable and connected the Arduino Pro Mini to the Dragon. 










I was able to read the flash program memory and save it in a HEX file. However, I was unable to run the code in the debugger. I think the HEX file needs to be converted to a .COF, .D90, .ELF, .OBJ, or .DBG file. I had a difficult time converting the HEX file to BIN format so that I could see the text characters in the program code. I wound up importing the HEX file into an MPLABX project, and then looking at the program memory. Here are some snippets of display text:


```
32316400      6961665F      6572756C      3164003A      .d12_fai lure:.d1             
                31702D32      6165725F      676E6964      3164003A      2-p1_rea ding:.d1             
                32702D32      6165725F      676E6964      3264003A      2-p2_rea ding:.d2             
                61665F30      72756C69      74003A65      6C61746F      0_failur e:.total             
                5F6F695F      6C696166      73657275      7276003A      _io_fail ures:.vr             
                665F6665      756C6961      003A6572      66657276      ef_failu re:.vref             
                6165725F      676E6964      0064003A      000F000E      _reading :.d.....
```


```
91F49005      94092DE0      CFFF94F8      66657276      .....-.. ....vref             
                74756F20      65646973      6D696C20      00737469       outside  limits.             
                66657276      736E6920      20656469      696D696C      vref ins ide limi             
                50007374      20747261      3D410031      42202C30      ts.Part  1.A=0, B             
                4100313D      30324344      003A003A      74726150      =1.ADC20 :.:.Part             
                41003220      202C313D      00303D42      6C696166       2.A=1,  B=0.fail             
                3A657275      0E006400      10000F00      03001200      ure:.d.. ........
```
I thought there would be more display text, but I don't really know if this code even works. It is what I found in the charger I got from JKN.

[edit] BTW, I found out that the Dragon will not work with the Arduino in the target. Some of the critical lines are connected to circuitry that loads them down, while others are unused. Obviously the design was not done with the intent of using a professional ICSP debug/programming tool. The FTDI seems not as versatile.

Mostly, D13-SCK is used for BMSIN, D12-MISO is used for EOCOUT. D11-MOSI and RST- seem unused.


----------



## PStechPaul (May 1, 2012)

Here is the display with the original code as I received it and saved above:










I compiled and uploaded the sketch for 2013_07_31_V12 using Arduino 1.0.6 and I got the following displays:


----------



## PStechPaul (May 1, 2012)

If anyone has the Arduino code in Intel Hex format, I have made a simple Hex2Bin program that displays the hex file and also the corresponding text, which may help identify the version (which may or may not be included in the sketch, but should be). Here is a screen shot:










If you would like to use this application, it is available for download here:

http://enginuitysystems.com/files/EMW/Hex2Bin.exe

The use of the AVR Dragon to extract the code from the Arduino is explained above.


----------



## dcb (Dec 5, 2009)

fyi, should be able to extract the bin without a dragon, just usb and bootloader (i.e. for an UNO):
avrdude -F -v -pm328p -cstk500v1 -P/dev/ttyUSB0 -b19200 -D -Uflash:rrogram.bin:r


----------



## PStechPaul (May 1, 2012)

I see that the : p translates to a stick-out-tongue emoticon in the command line you show above. Yes, I found similar information, and also some other command-line utilities that might translate Intel hex to binary, but I wanted a more convenient Windows-based GUI to do that. Here is info:

http://www.evilmadscientist.com/2011/avr-basics-reading-and-writing-flash-contents/

It should be simple enough to write a shell program that essentially generates the command line and then reads and displays the contents of the generated BIN or HEX file. Here is the documentation for the AVRdude:

http://www.nongnu.org/avrdude/user-manual/avrdude_4.html

I was unable to determine the programmer to use, which you show to be stk500v1, or Atmel STK500 Version 1.x firmware. I don't know where you got that? I thought it should be FTDI, which may be:


```
avrftdi          FT2232D based generic programmer
```


----------



## PStechPaul (May 1, 2012)

It is now time to do some testing of the charger. I first made a copy of the working V13 firmware as a starting point, and when I started the Arduino IDE it asked me to upgrade from 1.0.6 to 1.6.4. So I did, and I even contributed $5 to the project. However, when I tried to compile it, it didn't find the "MemoryFree.h" file. When I commented it out, it did not throw an error, but seemed to hang up on the compile.

I tried a simple "Test.ino" sketch, and it compiled OK, but when I tried to upload it, there were error messages from avrdude about being unable to sync. So I went back to 1.0.6 and all seems good.

I added the following code as a way to explore the means to write to the display:


```
prog_char msg_lcd_15[] PROGMEM = "P S Tech Test 1";

 
 int x = 0;
    
  if(LCD_on) {  
    myLCD->clrScreen();
    myLCD->setOpacity(1);
  } else {
    state=STATE_DONE; // skip config altogether if no LCD
  }    
  
  printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_PSTECH);
   while(1)
{
  sprintf(str, "count = %d", x); myLCD->printStr(0, 8, 2, 0, 0, 0x1f, str);
  x++;
  delay(100);
}
```
I had some problems with the display showing the text incorrectly or in the wrong position, and I also had problems with the programmer not being in sync. This may have been because the display and backlight is being driven from the USB port and may be too much load. It worked better when I turned off the display with the ON/OFF switch on the charger. But it would come back up in random condition. The only reliable way to get a good reset of the display and proper operation seemed to be to break the USB connection.


----------



## PStechPaul (May 1, 2012)

I was able to add some code to read the mv_in (Mains Voltage IN) pin, and show the value on the display with various AC voltages applied to the charger input:


```
while(1)
    {
    fpnum = (float)analogRead(pin_mV)*5.0/1024;
    dtostrf(fpnum, 6, 2, str);
    myLCD->printStr(0, 6, 2, 0, 0, 0x1f, str);
    sprintf(str, "count = %d", x); myLCD->printStr(0, 8, 2, 0, 0, 0x1f, str);
    x++;
    delay(100); }
```
One thing I noticed is that the count "x" seems to update at about 300 mSec intervals, so the rest of the code takes about 200 mSec. Probably a lot of overhead in the floating point routines.


----------



## John N (Sep 25, 2013)

Paul,

I think the not in sync message is actually saying that it is not able to talk. Since the display and the programmer are both communicating with the Arduino via the same port they can't both be connected at the same time. That is the reason for the power switch on the display, to turn it off when programming. Then the Arduino needs to be reset when the display is re-powered to initiate the communications with the display again. I think there is some code in the beginning of the EMW sketches that is checking for the response from the display and goes into programming mode if it doesn't see a response within some time limit.

I believe the MemoryFree.h is a "standard" library file and a web search should turn it up. I don't know if it is somehow linked or limited to the older versions of Arduino or not. I'll send a zip file over so you can have it if you need it.

respectfully,
John


----------



## PStechPaul (May 1, 2012)

I saw a similar sync error with the 1.0.6 software, so you are probably right that it was a comm error and turning off the display was needed to avoid that when programming. I never know why it had an on/off switch - now I know!

The MemoryFree.h file must be part of the older framework, but actually I found that it is not even needed, nor is pgmspace.h which was in the EMW sketch. I think Valery may have had issues with running out of memory and used the functions while debugging and updating the software.

I plan to pare down the EMW_Charger_Test sketch to bare minimum so it will be stupid easy to understand and modify. It should be able to apply 120 VAC input and a 300 ohm 91W resistive load (or maybe a 12 ohm 20W resistor, or a few in parallel) so that I can conduct a series of tests that should exercise the various I/O pins and basic functions of the charger. They are:


```
//---------------- pin-out constants ----------------
//========== analog pins
const byte pin_C=0; // output current pin
const byte pin_bV=1; // output / battery voltage pin
const byte pin_heatSinkT=2; // charger heatsink temp - for thermal derating 
const byte pin_12Vsense=3; // implementing undervoltage protection
const byte pin_temp2=4; // 4 - spare prewired as temp input
const byte pin_mV=5; // A5 for mains voltage sense
const byte pin_mC=7; // will only work in V12 control boards (June 2013). needed only for full digital PFC control
//========== digital pins
// 0/1 reserved for serial comms with display etc
const byte pin_pwrCtrlButton=2; // this is wired to the button (used for menu step)
const byte pin_pwrCtrl2Button=3; // this is wired to the button2 (used for menu select)
const byte pin_inrelay=4; // precharges input caps - normally pin 4, in some units pin 6 running fan relay
const byte pin_outrelay=5; // protects from reverse polarity on traction battery, precharges output resistors
const byte pin_PWMpulldown=6; 
const byte pin_J1772=7; // J1772 pilot input. 1k is hardwired on V14+ pcbs so J1772 will power on on connect
const byte pin_fan=8; // fan control - this is pin4 in all kits shipped before Mar '2912
const byte pin_PWM=9; // main PWM pin
// max current reference voltage (using PWM) -  was 6 in the V13 pcb (kits shipped before March 2012)
// now moved to pin 10 so that we can use higher PWM frequency 20kHz PWM
const byte pin_maxC=10; 
// 110/220vac relay control - for non-PFC units only
// If PFC is connected, relay would never close so can be removed
// also can be left unused / unconnected if separate 110V / 220V inputs are used 
const byte pin_110relay=11; // in kits shipped before Mar 2012, this is pin 5
const byte pin_EOC=12; // end-of-charge output (see pinout diagram) - pulled low when charge is complete
// end-of-charge input from BMS. Pull low / disconnect from positive TTL signal to activate
//     (normallly will be realized via connecting NC BMS loop between this pin and EOC pin (or +5V)
const byte pin_BMS=13; 
//---------------- END PINOUTS -----------------------
```
=============================================
Only a few of these are actually needed. Analog inputs:

pin_C will read output current as sensed by the Hall Effect sensor

pin_bv will read output (battery) voltage by the A7520 isolated sensor

pin_heatSinkT may or may not be implemented

pin_12Vsense may or may not be implemented

pin_mV has already been discussed above

pin_mC will only work in V12 control boards (June 2013) for PFC

=============================================
Digital pins:

pin_pwrCtrlButton input for menu step button (active high)

pin_pwrCtrl2Button input for menu select button (active high)

pin_PWMpulldown output for PWM shut-down (active high)

pin_J1772 input for J1772 pilot input.

pin_PWM output for main PWM 

pin_maxC output may be a PWM level to the (+) input of the comparator U7

pin_EOC output active low (end of charge)

pin_BMS input from BMS

=============================================

The 120 VAC input should provide an analog signal on pin_mv. The pin_PWM can be turned on to charge the output capacitors through toroid inductor L2 which should provide an output voltage that can be read on pin_bv. With a 300 ohm load on the output, about 0.5 amps might be detected by the Hall sensor.

The PWM pin might be set to 10% or whatever value and this should provide a current into the 12 ohm load (or multiples thereof) to get several amps.


----------



## PStechPaul (May 1, 2012)

I was unable to get any output from the charger with the PWM set as follows:


```
setPwmFrequency(9, 8);  // 31250/8=3906, Timer1 pins 9 & 10, 31250 Hz, divisors 1, 8, 64, 256, 1024
   while(1)
    {
    analogWrite(pin_PWM, 12); // 12/255 approx 5%
    analogWrite(pin_maxC, 128); // 128/255 approx 50%
    digitalWrite(pin_PWMpulldown, LOW); // enable PWM
```
I removed the driver board from the power board (just the four quick-connects), and I tried to measure the gate voltage. I got nothing. Then I tried to measure the outputs of the DC-DC converter, and I think I heard a pop, and then smelled the component getting hot. It was "toast". Here is what I was working with - spaghetti!










This is the driver board:










Fully removed, bottom side, showing excess flux and poor soldering. But it is difficult to solder (and harder to unsolder) because of the wide traces and general lack of thermal spokes:










I removed the blown DC-DC converter, which required breaking it and removing pins. I could not clear the ground pin holes. Also, I noticed that the A3120 was loose in its socket, and found that it was not an IC socket, but two female header strips. As you can see, not designed for IC pins:










I used a small drill to clear the holes, and also defluxed with alcohol and cleaned the board with detergent, hot water, and hot air:










I added 15 V zeners across the output of the DC-DC converter. It was putting out 37 VDC and the A3120 has a maximum rating of 35V:










I hooked the scope on the gate pins and here is the waveform:


----------



## PStechPaul (May 1, 2012)

Here is a simulation of what I should expect with the 5% PWM duty cycle for the charger, into a 300 ohm load, with 120 VAC on the input (150 VDC):










Here it is at 50%, with a 24 VDC battery as load. It is operating as a true buck converter, drawing about 41 amps and charging with 80 amps. The inductor is running in continuous mode, from 49 amps to 120 amps peak:










For 350 VAC input, a duty cycle of 12.5% provides about 27 amps charge. 










It seems like it could be difficult to implement an adequate control loop with only reading battery voltage and battery current, especially when the signals are heavily filtered. The current rises to its target value in only about 5 mSec, and the analog filtering seems OK with a TC of about 10 uSec, but it seems that the charger firmware reads the current and takes a rolling average in a loop that does lots of other things, so I feel that it could be several hundred milliseconds between readings under some circumstances, and by that time all hell can break loose! But I have not yet really gotten into the software, and what I have seen fills me with dread...


----------



## PStechPaul (May 1, 2012)

Time for a quick look at software (firmware), particularly the display functions, which seem to take an inordinately long time. Valery has implemented his own uLCD_144_SPE library which sends bytes to the display via the serial port and looks for an ACK in return, or else times out after about 500 mSec. Here is the EMW library:


```
//==================================== SCREEN FUNCTION LIBRARY ========================
//-------------- define all function members ----------    
uLCD_144_SPE::uLCD_144_SPE(int baud) {
  Serial.flush();
 
  delay(3000);
 
  Serial.begin(baud);
   // clr screen here - do NOT call function as we need to get an Ack here 
  Serial.print((char)0xFF);
  Serial.print((char)0xD7);
  isAlive_=waitAck();
}
// process color data
byte uLCD_144_SPE::getMSB(byte red, byte green, byte blue) {
  return red*8 + green/8;
}
byte uLCD_144_SPE::getLSB(byte red, byte green, byte blue) {
  return (green & 0x7)*32 + blue;
}
int uLCD_144_SPE::waitAck() {
  int x=0;  
  int z=0;
  do {
    x=Serial.read();
    delay(5);
    z++;
  } while(x!=0x06 && z<100); // wait for screen acknowledgement or 100 cycles
 
  if(x==0x06) return 1; // LCD is there
  // LCD not there
  return 0;
}
int uLCD_144_SPE::isAlive() {
  return isAlive_;
}
void uLCD_144_SPE::clrScreen() {
  Serial.print((char)0xFF);
  Serial.print((char)0xD7);
  waitAck();
}  
void uLCD_144_SPE::setYspacing(int pixels) {
  Serial.print((char)0xFF);
  Serial.print((char)0x79);
  Serial.print((char)0x00);
  Serial.print((char)pixels);
  waitAck();
}
 
void uLCD_144_SPE::setOpacity(int opacity) {
  // 0 = transparent, 1 = opaque
  Serial.print((char)0xFF);
  Serial.print((char)0x77);
  Serial.print((char)0x00);
  Serial.print((char)opacity);
  waitAck();
}
void uLCD_144_SPE::moveCursor(int col, int row) {
  Serial.print((char)0xFF);
  Serial.print((char)0xE4);
  Serial.print((char)0x00);
  Serial.print((char)row);
  Serial.print((char)0x00);
  Serial.print((char)col);
 
  waitAck();
}
void uLCD_144_SPE::setFGcolor(byte red, byte green, byte blue) {
  Serial.print((char)0xFF);
  Serial.print((char)0x7F);
  Serial.print((char)getMSB(red, green, blue));
  Serial.print((char)getLSB(red, green, blue));
 
  waitAck();
}
void uLCD_144_SPE::setFont(int font) {
  Serial.print((char)0xFF);
  Serial.print((char)0x7D);
  Serial.print((char)0x00);
  Serial.print((char)0x00);
 
  waitAck();
}
void uLCD_144_SPE::printStr(int col, int row, int font, byte red, byte green, byte blue, const char *str) {
  moveCursor(col, row);
  setFont(0);
  setFGcolor(red, green, blue);
  setYspacing(2);
 
  Serial.print((char)0x00);
  Serial.print((char)0x06);
  Serial.print(str);
  Serial.print((char)0);
  waitAck();
}
void uLCD_144_SPE::printStr(int col, int row, int font, byte red, byte green, byte blue, const prog_char *str, int type) { // has to have a different sig from the above function
  moveCursor(col, row);
  setFont(0);
  setFGcolor(red, green, blue);
  setYspacing(2);
 
  Serial.print((char)0x00);
  Serial.print((char)0x06);
  Serial.print(str);
  Serial.print((char)0);
  waitAck();
}
void uLCD_144_SPE::wrapStr(const char *s, char *sto, int lineSize, const char *prefix) {
    const char *head = s;
    int pos, lastSpace;
    pos = lastSpace = 0;
    while(head[pos]!=0) {
        int isLf = (head[pos]=='\n');
        if (isLf || pos==lineSize) {
            if (isLf || lastSpace == 0) { lastSpace = pos; } // just cut it
            if (prefix!=NULL) { sprintf(sto, "%s%s", sto, prefix); }
            while(*head!=0 && lastSpace-- > 0) { sprintf(sto, "%s%c", sto, *head++); }
            sprintf(sto, "%s\n", sto);
            if (isLf) { head++; } // jump the line feed
            while (*head!=0 && *head==' ') { head++; } // clear the leading space
            lastSpace = pos = 0;
        } else {
            if (head[pos]==' ') { lastSpace = pos; }
            pos++;
        }
    }
    printf("%s\n", head);
}
```
There may be a problem in the waitAck function (among others). For one thing, it continues to read the serial port expecting an ACK, and does not look for a NAK, and assumes the display is not present if the ACK is not received. This return value is used only during initialization, where the CLRSCR bytes are sent and the ACK sets the global isAlive_ variable. 

I had to search for the details of the display command set, and found one document here:
http://old.4dsystems.com.au/downloa...X-SGC/Docs/GOLDELOX-SGC-COMMANDS-SIS-rev6.pdf

The start-up sequence for the display should include a wait period of about 1 second, during which time garbage might be transmitted and should be ignored (buffer flush). Then an autobaud command should be sent, and then an ACK should be received, after which normal communication may proceed. I think the EMW sequence is incorrect - see above. It sends 0xFF and 0xD7, which I cannot find as a valid command. [edit] This is the OLD command set. See below.

Also, I don't know how long it takes between sending a command and receiving the ACK or NAK, but it may be prudent to wait 5 mSec _before_ reading the serial port. It probably does no good to continue repetitive Serial.read() calls if the COM buffer has already been read.

The idea of turning off the power to the display to enable programming may not be the best option. The powered-down display may place a load on the TX and RX lines, and when the display is powered up after programming, there seems to be nothing in the firmware to detect this and initialize it. Much better may be to disconnect or disable the TX and RX lines and also implement a RESET on the Arduino when the display is powered up. 

Of course I am not an Arduino expert so I may be wrong. I welcome comments!

[edit] It appears that the other library uLCD_144 library has the commands as shown in the Rev6 document I linked above. Here is a more recent document showing the revised command set:

https://www.parallax.com/sites/default/files/downloads/28081-Goldelox-Spe-Command-set-v1.3.pdf

I also found a command library for the Arduino that may work better than Valery's:

https://github.com/4dsystems/Goldelox-Serial-Arduino-Library


----------



## PStechPaul (May 1, 2012)

Another tiny but significant step. I replaced the driver board in the charger and applied a very low current limited voltage to the mains input via the 50 VA PT, probably about 40 VAC. I stepped the PWM from 0 to 12 (out of 255) and put an ammeter on the output. I was able to get up to about 3 amps although it was unsteady because of the wimpy supply. Here is the revised test code loop:


```
int x = 0, inum, PWMval=0;
  float fpnum = 0.0;
  char *s = "                ";
  //******************************** TEST ******************************************
  printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_PSTECH);
  setPwmFrequency(9, 8);  // 31250/8=3906, Timer1 pins 9 & 10, 31250 Hz, divisors 1, 8, 64, 256, 1024
   while(1)
    {
    if(PWMval < 12)
      PWMval++;
    else
      PWMval = 0;
    analogWrite(pin_PWM, PWMval); // 51/255 approx 20%
    analogWrite(pin_maxC, 128); // 128/255 approx 50%
    digitalWrite(pin_PWMpulldown, LOW); // enable PWM
    inum = analogRead(pin_mV); //Read Mains voltage
    s = itoa(inum, s, 10);
    myLCD->printStr(0, 2, 2, 0, 0, 0x1f, s);
    inum = analogRead(pin_bV);  //Read battery voltage
    s = itoa(inum, s, 10);
    myLCD->printStr(0, 4, 2, 0, 0, 0x1f, s);
    inum = analogRead(pin_C);  //Read current
    s = itoa(inum, s, 10);
    myLCD->printStr(0, 6, 2, 0, 0, 0x1f, s);
    s = itoa(PWMval, s, 10);
    myLCD->printStr(0, 8, 2, 0, 0, 0x1f, s);
    x++;
    delay(2000);
    }
```
I took a short video of the test. Not much to see, but it is working and if you look closely you can even see the current reading change on line 6 along with the PWM value on line 8. The reading is not well formatted so 0,1,2 will appear as 02,12,22.

http://enginuitysystems.com/files/EMW/EMW_Charger_Test_2072.AVI


----------



## PStechPaul (May 1, 2012)

It took a while, and some serious head scratching, but I was able to get a demo for the display library to compile and run on the charger. So when I redo the software, it can have some nice graphics and decent size text:




























The demo is contained in the Goldelox serial command package that is available on the 4D Systems website. Link is in a previous post above.

Figured I might as well show my logo while I'm at it: 










Also added a progress bar. Should look nice to show SOC of the battery:

http://enginuitysystems.com/files/EMW/uLCD_144_SPE_Test_2078.AVI


----------



## PStechPaul (May 1, 2012)

At this point I'd like to walk through some of the EMW software. I will use V12 as that is what I was able to compile and load on the charger I am working on, which has a V12 control board and a V13 driver. They appear to be paired as shown here:









It may help that I have annotated the board layout as well as the schematics, and they will be necessary to understand the software. Here is the control PCB:









And the driver:










Actually in this post I will just point out a few items of the hardware design that I think need to be understood and modified to proceed with the software analysis and rewrite.

On the *control board*, the use of the comparator and the various transistors to change logic levels seems awkward and prone to failure, but it seems to work so I won't do much with it. I don't like the idea that the gate for the output PWM is turned ON via the 5 VDC supply and 330 ohm resistor R44, and is turned OFF by the activation of the shutdown signal to S6 (which is not labeled and may not be populated on many boards), and the output of the comparator U9. 

Another deficiency may be the implementation of the pushbuttons with pull-down resistors of 10k and no capacitors to reduce contact bounce. Something like 10 nF capacitors can be easily added, and I think I'll do that.

A reset pushbutton may be very helpful to start up after programming and re-powering the display. It may also be useful to disconnect the TX and RX lines for programming, rather than removing power to the display. But that is minor and need not be fixed.

The *driver board* seems to be where much work is needed. As mentioned above, the DC-DC converters can put out more than their rated 15 VDC, which could stress the A3120 IGBT drivers. There should also be a bidirectional TVS diode across the gates, and possibly also a small capacitor to soften the drive signal and reduce high voltage transients and ringing. But it may be better to put some of these components on the power board directly on the IGBT gates.

The use of the optocoupler PC817 for mains voltage measurement has been discussed before, and really should be replaced with an A7520 or other linear isolated voltage measurement means.

There are various issues with the PFC circuit, also discussed previously, and in particular the short piece of copper wire used as a current shunt for Isen is totally inadequate. But the circuit seems to work in spite of that design flaw, so perhaps it can be ignored for now.


----------



## PStechPaul (May 1, 2012)

OK, now for the software. This will probably require several posts. The stated software structure is as follows:


```
----------- Basic code structure:
------ Startup: 
* check for LCD presence. if none (programming button in the programming state), launch in the serial-controlled mode 
* 2 timeouts - one 5 sec for config, one 10 sec for power setting. can be interrupled by any button
* check mains voltage. If 110, limit power to ~1.5kW
* set duty cycle to 0
------ Charging (CV or CC):
* increase duty cycle until the condition is met (target voltage or target current)
* monitor condition by taking frequent samples averaging over 120Hz ripple waveform
* based on average value of condition, change duty cycle accordingly (slow down update frequency
  as we get closer to the target voltage)
* break when exit condition satisfied or stop / pause commands received
```
There are all sorts of #define statements for various configurations up to line 131, and then this:

```
// LCD includes - have to be here in the code as they depend on some switches above
// LCD library for 4D systems display ([URL]http://www.4dsystems.com.au/prod.php?id=121[/URL])
#ifdef LCD_SPE
  #include <uLCD_144_SPE.h>
  uLCD_144_SPE *myLCD;
#else
  #include <uLCD_144.h>
  uLCD_144 *myLCD;
#endif
int LCD_on=0; // this defines manual vs serial-controlled operation
int cmd[2]={0, 0}; // command variables, used in serial comms
```
Here are definitions for various messages. I have edited the code for brevity:

```
//============================================== define messages ==================
// using program memory as we are now running out of SRAM...
// see [URL]http://arduino.cc/en/Reference/PROGMEM[/URL] for reference
// some additional good memory tips at [URL]http://liudr.wordpress.com/2011/02/04/how-to-optimize-your-arduino-memory-usage/[/URL]
const byte MSG_THX = 0x00;
const byte MSG_NOBATT = 0x01;
const byte MSG_WRONGPROF = 0x02;
const byte MSG_BMSSTOP = 0x03;
const byte MSG_TIMEOUT = 0x04;
const byte MSG_USRPAUSE = 0x05;
const byte MSG_LOSTIN = 0x06;
const byte MSG_SENSEERROR = 0x07;
const byte MSG_NORMEXIT = 0x08;
const byte MSG_DONE = 0x09;
const byte MSG_10 = 0x0A;
const byte MSG_11 = 0x0B;
 
#ifndef LCD_SPE
#else 
  prog_char msg_long_0[] PROGMEM = "Thank you for \nchoosing EMW! \nAny BTN to CFG";
  prog_char msg_short_0[] PROGMEM = "INIT";
  prog_char msg_long_1[] PROGMEM = "No batt or reverse! \nANY BTN to ignore";
  prog_char msg_short_1[] PROGMEM = "NOBATT";
  prog_char msg_long_2[] PROGMEM = "Wrong profile!";
  prog_char msg_short_2[] PROGMEM = "WRONGPROF";
  prog_char msg_long_3[] PROGMEM = "BMS Stop";
  prog_char msg_short_3[] PROGMEM = "BMSSTOP";
  prog_char msg_long_4[] PROGMEM = "Timeout";
  prog_char msg_short_4[] PROGMEM = "TIMEOUT";
  prog_char msg_long_5[] PROGMEM = "Paused. RED BTN \nto exit, GRN to \nresume";
  prog_char msg_short_5[] PROGMEM = "USRPAUSE";
  prog_char msg_long_6[] PROGMEM = "Lost AC";
  prog_char msg_short_6[] PROGMEM = "LOSTIN";
  prog_char msg_long_7[] PROGMEM = "Sensor/cal error. \nRecal/chk wiring";
  prog_char msg_short_7[] PROGMEM = "SENSEERROR";
  prog_char msg_long_8[] PROGMEM = "Step complete";
  prog_char msg_short_8[] PROGMEM = "NORMEXIT";
  prog_char msg_long_9[] PROGMEM = "Complete! GRN BTN \nto repeat";
  prog_char msg_short_9[] PROGMEM = "DONE";
  prog_char msg_long_10[] PROGMEM = "X";
  prog_char msg_short_10[] PROGMEM = "X";
  prog_char msg_long_11[] PROGMEM = "X";
  prog_char msg_short_11[] PROGMEM = "X";
#endif
 
PROGMEM const char *msg_long_table[] =    
{   
  msg_long_0,
  msg_long_1,
  msg_long_2,
  msg_long_3,
  msg_long_4,
  msg_long_5, 
  msg_long_6,
  msg_long_7,
  msg_long_8,
  msg_long_9,
  msg_long_10,
  msg_long_11
};
 
PROGMEM const char *msg_short_table[] =    
{   
  msg_short_0,
  msg_short_1,
  msg_short_2,
  msg_short_3,
  msg_short_4,
  msg_short_5, 
  msg_short_6,
  msg_short_7,
  msg_short_8,
  msg_short_9, 
  msg_short_10,
  msg_short_11
};
 
const byte MSG_LCD_CELLTYPE = 0x00;
const byte MSG_LCD_CV = 0x01;
const byte MSG_LCD_NCELLS = 0x02;
const byte MSG_LCD_CAPACITY = 0x03;
const byte MSG_LCD_CAL0 = 0x04;
const byte MSG_LCD_CAL1 = 0x05;
const byte MSG_LCD_CAL2 = 0x06;
const byte MSG_LCD_CONFIRM = 0x07;
const byte MSG_LCD_PARAMS = 0x08;
const byte MSG_LCD_CFG = 0x09;
const byte MSG_LCD_TOPMENU = 0x0A;
const byte MSG_LCD_INC = 0x0B;
const byte MSG_LCD_OUTC = 0x0C;
const byte MSG_LCD_TOUT = 0x0D;
const byte MSG_LCD_RUN = 0x0E;
const byte MSG_LCD_BLANK = 0x0F;
 
#ifndef LCD_SPE
#else
  prog_char msg_lcd_0[] PROGMEM = "Cell Type:       ";
  prog_char msg_lcd_1[] PROGMEM = "CV cutoff:       ";
  prog_char msg_lcd_2[] PROGMEM = "Number of cells: ";
  prog_char msg_lcd_3[] PROGMEM = "Capacity:        ";
  prog_char msg_lcd_4[] PROGMEM = "Calibrated zero";
  prog_char msg_lcd_5[] PROGMEM = "Connect batt. BTN \nto skip";
  prog_char msg_lcd_6[] PROGMEM = "Enter actual \nbatt voltage:";
  prog_char msg_lcd_7[] PROGMEM = "Confirm:      ";
  prog_char msg_lcd_8[] PROGMEM = "Params      ";
  prog_char msg_lcd_9[] PROGMEM = "press BTN to\n adjust";
  prog_char msg_lcd_10[] PROGMEM = "Action:                   ";
  prog_char msg_lcd_11[] PROGMEM = "max INput current ";
  prog_char msg_lcd_12[] PROGMEM = "max OUTput current";
  prog_char msg_lcd_13[] PROGMEM = "timeout (#min or 0):";
  prog_char msg_lcd_14[] PROGMEM = "Confirm CHARGE:";
  prog_char msg_lcd_15[] PROGMEM = "[           ]";
#endif
 
PROGMEM const char *msg_lcd_table[] =    
{   
  msg_lcd_0,
  msg_lcd_1,
  msg_lcd_2,
  msg_lcd_3,
  msg_lcd_4,
  msg_lcd_5,
  msg_lcd_6,
  msg_lcd_7,
  msg_lcd_8,
  msg_lcd_9,
  msg_lcd_10,
  msg_lcd_11,
  msg_lcd_12,
  msg_lcd_13,
  msg_lcd_14,
  msg_lcd_15
};
//=========================== end define messages ==================================
```
I'll try to explain what is being done here. It seems rather awkward and there must surely be a better way!

AIUI, there are 12 messages used during initialization, which can be "long" or "short" (possibly for a 16 or 20 character display). Then there are two tables which contain the addresses of these long and short messages in PROGMEM for later retrieval and display. This seems a bit convoluted and perhaps a better way to do this would be as follows:


```
typedef enum  {MSG_INIT, MSG_NOBATT, MSG_WRONGPROF} tSTARTUP;
String msg_long[][12] = { "Thank you for \nchoosing EMW! \nAny BTN to CFG", "No batt or reverse! \nANY BTN to ignore", "Wrong profile!" };
tSTARTUP msgid = MSG_NOBATT;
Display.putstr((char*)msg_long[msgid]) ;
```
That does compile, and should run OK, depending on how the indexing of strings (of variable length) is handled. It should work fine if messages are padded to 16 characters, which is the limitation of the display. The strings have NEWLINEs added to avoid overrun or wrapping of text. But it may be better to keep all messages within the 16 char limit, and possibly writing more than one line in some cases.

Actually this is what works:

```
typedef enum  {MSG_INIT, MSG_NOBATT, MSG_WRONGPROF} tSTARTUP;
char* msg_long[] = { "Message INIT", "Message NOBATT", "Wrong profile!" };
tSTARTUP msgid = MSG_NOBATT;
Display.txt_MoveCursor(11, 1) ;
Display.putstr(msg_long[msgid]) ;
```
Next, up to line 355 out of 1819, are the pinouts:


```
//---------------- pin-out constants ----------------
//========== analog pins
const byte pin_C=0; // output current pin
const byte pin_bV=1; // output / battery voltage pin
const byte pin_heatSinkT=2; // charger heatsink temp - for thermal derating 
const byte pin_12Vsense=3; // implementing undervoltage protection
const byte pin_temp2=4; // 4 - spare prewired as temp input
const byte pin_mV=5;
const byte pin_mC=7; // will only work in V12 control boards (June 2013). needed only for full digital PFC control
//========== digital pins
// 0/1 reserved for serial comms with display etc
const byte pin_pwrCtrlButton=2; // this is wired to the button (used for menu step)
const byte pin_pwrCtrl2Button=3; // this is wired to the button2 (used for menu select)
const byte pin_inrelay=4; // precharges input caps - normally pin 4, in some units pin 6 running fan relay
const byte pin_outrelay=5; // protects from reverse polarity on traction battery, precharges output resistors
const byte pin_PWMpulldown=6; 
const byte pin_J1772=7; // J1772 pilot input. 1k is hardwired on V14+ pcbs so J1772 will power on on connect
const byte pin_fan=8; // fan control - this is pin4 in all kits shipped before Mar '2912
const byte pin_PWM=9; // main PWM pin
// max current reference voltage (using PWM) -  was 6 in the V13 pcb (kits shipped before March 2012)
// now moved to pin 10 so that we can use higher PWM frequency 20kHz PWM
const byte pin_maxC=10; 
// 110/220vac relay control - for non-PFC units only
// If PFC is connected, relay would never close so can be removed
// also can be left unused / unconnected if separate 110V / 220V inputs are used 
const byte pin_110relay=11; // in kits shipped before Mar 2012, this is pin 5
const byte pin_EOC=12; // end-of-charge output (see pinout diagram) - pulled low when charge is complete
// end-of-charge input from BMS. Pull low / disconnect from positive TTL signal to activate
//     (normallly will be realized via connecting NC BMS loop between this pin and EOC pin (or +5V)
const byte pin_BMS=13; 
//---------------- END PINOUTS -----------------------
```


----------



## PStechPaul (May 1, 2012)

```
//============= BATTERY INFO  =====
struct config_t {
  int battType;
  int nCells;
  int AH;
  int CV; // per cell
  int CC; // max output current
  int mainsC; // max input current
  // sensor config
  float Vcal;
  float Vcal_k;
  float Ccal;
} configuration;
#ifdef NiXX
  // Nickel chemistries dVdt cutoff
  const float dVdt_stop=0.; // in %/s. at 1C, safe value is between -1E-05 and +1E-05
#endif
 
// DO NOT CHANGE THESE!
const int minMains=30; // min mains voltage to (1) test sensor connectivity and (2) detect mains disconnect 
const float min_CV_Crating=0.05; // wait until the current goes to XC (use values from your battery's datasheet)
// spread for duty cycle ramp conditions to avoid jitter - in volts
// With 10k resistor, 40ma sensor, 51V/A constant, voltage sensitivity is ~1V so no point setting this lower
const int spreadV=2.;
// With 50A sensor, 0.06V/A constant, current sensitivity is ~0.1A. But current being off is not a big deal...
const int spreadC=1.; 
float maxOutV=0; // absolute maximum output voltage - will be set later in the code
int minBattV;
#ifdef PFC
  const float charger_efficiency=0.93; 
#else
  const float charger_efficiency=0.95; 
#endif
// ------------------------------- END battery constants -----------------------------
```
Seems odd to hard code charger efficiency. It should be able to measure it and report it, but since it lacks the input current and voltage readings, it can't... 


```
//---------------- MAX CURRENTS
// input currents (used only for DC-DC units (if DCinput switch is active)
#ifdef DCinput
  float MAXinputC=100;
#endif
// absolute maximum average output current (used in CV mode) - leave at 0 here - will be set via power menu
float maxOutC=0.; 
#ifdef MCC100A
  float absMaxChargerCurrent=100; // 100A rating with high-current output toroid inductor
#else
  #ifdef buck_Ecore
    float absMaxChargerCurrent=40; // 40A default rating with old Ecore inductors
  #else
    float absMaxChargerCurrent=70; // 70A default rating with new toroid inductors
  #endif
#endif
#ifdef PFCdirect
  float absMaxChargerPower=25000; // 25kW rating for PFCDirect units with new 5-6" toroid inductors
#else
  #ifdef debugpower
    float absMaxChargerPower=20000; // 20kW for testing
  #else
    float absMaxChargerPower=12000; // 12kW rating for regular units with new 4" toroid inductors
  #endif
#endif
// when does the current limiter kick in? 1.2-1.3 is a good compromise to get the most out of 
// the input caps while keeping overall ripple below 50% 
// this is mostly relevant for 120Hz ripple. Switching frequency ripple is controlled by the automatic
// frequency selection and low-ESR high-freq output cap
// if using smaller inductors (e.g., <200 uH), may have to use higher ratio here (e.g., 1.4-1.5)
const float instantMaxCRatio=1.6; 
int timeOut=0; // in min, 0 means no timeout
```
Also strange to estimate ripple current and peak inductor current. 


```
//------------- THERMAL DERATING OF CHARGER 
// for now, simple protection by pausing charger until cooldown to certain temp
// note that heatSink temp at the point of measurement is generally 20-30 deg C LOWER than temperature 
// of critical components attached to heatsink (due to distance from components to probe)
// use maxHeatSinkT of <60 to ensure <85 deg C temp of components
// this assumes thermistor placement near the heat generating components
// BTW, modest airflow (a single 120mm PC fan) with a large (8x10x3" heatsink should be sufficient for 
// up to 30A output at max power 
#ifndef MCC100A
  const byte maxHeatSinkT=55; // in Centigrades - will start derating here
#else
  const byte maxHeatSinkT=47; // more aggressive derating at high current output
#endif
const byte ABSmaxHeatSinkT=85; // in Centigrades - will stop the charger altogether here
byte midHeatSinkT=45; // turn on the fans here; also wait until cool down to this temp before resuming at the prev power level 
byte lowHeatSinkT=35; // turn off the fans here 
//--------------------------------------------------------
// sensor supply
const float Vcc=5.0; 
float V_o_V0=0.; // 0.0 for voltage transducers (ISO124)
float V_o_C0=Vcc/2;
const float Aref=Vcc; 
 
//=============== voltage dividers settings ===========================
const float gain_7520=Vcc/0.512*0.99; // per datasheet, assuming Vref=Vcc, accounting for input resistance
//--------- mains voltage 
#ifdef PFCdirect
  const float upperR0_mV=2400.; // 2.4M in PFC direct units to extend sensing to 420V
#else
  const float upperR0_mV=2000.; // 2M in regular units
#endif
float divider_k_mV=-1.; 
#ifdef A7520_mV
  // resistor from -5V regulator; should form a ~-.25V divider together with the 
  // bottom resistor => >20x * bottom resistor 
  // for 2.7k bottom resistor, pick between 60k and 82k; 68k is a good choice... 
  const float V_o_mV0=Vcc/2-Vcc*2.7/68.*gain_7520; // -5V input, 2.7k bottom resistor, ~10x gain; // ~2.5V for A7520
  const float lowerR0_mV=2.7*gain_7520; // +-0.256V range for input, ~10x gain, 2.7k bottom resistor
  const float lowerR_mV=lowerR0_mV;
#else
  const float V_o_mV0=V_o_V0;
  const float lowerR_mV=23.79; // 1/(1/27.+1/200.)
#endif
 
//--------- battery voltage 
#ifdef PFCdirect
  const float upperR0_bV=2400.; // 2.4M in PFC direct units to extend sensing to 420V
#else
  const float upperR0_bV=2000.; // 2M in regular units
#endif
float divider_k_bV=-1.;
#ifdef A7520_V
  // resistor from -5V regulator; should form a ~-.25V divider together with the 
  // bottom resistor => >20x * bottom resistor 
  // for 2.7k bottom resistor, pick between 60k and 82k; 68k is a good choice... 
  const float V_o_bV0=Vcc/2-Vcc*2.7/68.*gain_7520; // -5V input, 2.7k bottom resistor, ~10x gain; // ~2.5V for A7520
  const float lowerR0_bV=2.7*gain_7520; // +-0.256V range for input, ~10x gain, 2.7k bottom resistor
  const float lowerR_bV=lowerR0_bV;
#else
  const float V_o_bV0=V_o_V0;
  const float lowerR0_bV=27.; // 27k
  const float lowerR_bV=23.79; // in parallel with 200k input resistance of the iso124
#endif
float V_o_bV=V_o_bV0;
//==================================== end voltage dividers setup =========================
 
//=================================== charger current sensor ==============================
// V/A constant for the charger output current sensor 
// some small value so that we don't overcurrent by mistake. this will be replaced later in code
float k_V_C=0.01; 
float k_V_mC=0.01; 
float V_o_C=0.6; 
float V_o_mC=0.6; 
//=================================== END charger current sensor ==========================
```


----------



## PStechPaul (May 1, 2012)

```
//===================== charger cycle timers =====================================
#define SLOWUPDATE // slow down update upon reaching the target condition
// for stepDelay=1000, use measCycle_len=300, dVdt_measCycles=150
// when changing stepDelay, change the other 2 variables so that stepDelay*measCycle_len = 0.5-1 sec
// and stepDelay*measCycle_len*dVdt_measCycles = 100-200 sec
const byte stepDelay0=4; // primary charger loop delay in millioseconds
const byte measCycle_len0=100; // how many primary loop cycles per display cycle
const byte nSamplesStopVar0=10; // how many samples for moving averages of output voltage / current
int stepDelay; // this will be changed in the loop
int measCycle_len; // this will be changed in the loop
byte nSamplesStopVar;
const byte stopCycles=5; // how many primary charger cycles to require stop condition to exist before exiting
const byte waitReadSamples=200; // wait between samples for voltage / current readouts in microseconds
const byte nReadSamples0=2; // how many samples to average in a single call of readX() functions
const byte nReadSamples_mV=50; // how many samples to average in a single call of read_mV() function
byte nReadSamples;
int CV_timeout=10; // what is the max duration (in mins) CV loop is allowed to spend below C stop; should be > ramp time of charger
//===================== end charger cycle timers =================================
//=========== these should be global vars
float duty=0, duty_crit=0;
float mainsV=0, outV=0, outC=0;
float outC_avg=0., outV_avg=0.;
byte charger_run=0;
unsigned long timer=0, timer_ch=0, timer_step=0;
float AH_charger=0;
unsigned int min_up=0;
char str[64];
byte state;
float temp;
#ifdef NiXX  
  //------------------------- Running Averages for dV/dt calcs -------------
  float V_ravg[2];
  unsigned long t_ms = 0;
  unsigned long ele_counter=0;
  float dVdt = 0.0;
#endif
//-----------------------Navigate Menu--------------------
const char * configMenu[] = { " Run  ", " Pwr  ", " Time "  };
const unsigned int configMenuLen = 3;
const char * menuNavigate[] = { " Yes ", " No " };
const unsigned int menuNavigateLen = 2;
// ------------- end global vars ---------------------------
```
Those are the various global variables and constants. Seems like a lot, and who knows if they are all actually needed, used, or even properly implemented?


```
//================================ ALL HELPER FUNCTIONS ======================
int runChargeStep(int cycleType, float CX, int stopType, float stopValue);
void stopPWM();
float readV();
float read_mV();
void setMaxC(float maxC);
float readC();
float read_mC();
int getNormT();
int read_T(byte pin);
float sampleRead(byte pin);
float get_k_V_C(int selection);
float get_V_o_C(int selection);
void resetDelayParams();
void printParams(float duty, float outV, float outC, int t, float curAH, float dVdt);
void printConstStr(int col, int row, int font, byte red, byte green, byte blue, const byte msg_id);
void printClrMsg(const byte msg_id, const int del, const byte red, const byte green, const byte blue);
void EMWserialMsg(const char *txt);
void readSerialCmd(int *cmd_);
char *ftoa(char *a, double f, int precision);
void updateMovingAverages(float V);
unsigned int MenuSelector2(unsigned int selection_total, const char * labels[]);
byte isBtnPressed();
int BtnTimeout(int n, int line);
int DecimalDigitInput3(int preset);
void printDigits(int start, int * digit, int stat);
void printDigit(int x, int stat, char * str);
//=================================== END helper functions ======================================
```
Those are the prototype declarations for the helper functions which are defined further along. We are now on line 581 and ready to explore the setup() and loop() functions which are part of the Arduino environment.


----------



## PStechPaul (May 1, 2012)

```
void setup() {
  // battery type 
// 0 = Lithium, 1 = NiMh/NiCad
const char * battTypeLabel[] = { "LiFePo4", "NiXX   " };
int battTypeLen = 2;
// min battery voltage - if below this, do not start the charger
const int minBattVs[2]={25, 9}; // in 0.1V units
// CV constant (N/A for LA, Ni)
const int CVs[4]={35, -10}; // in 0.1V units; charging voltage for CALB 3.6V per cell. Using 3.5 here to ensure reliable detecion of end-of-charge for a bottom-balanced pack
 
  #ifdef DEBUG0
    // double ratings for testing 
    absMaxChargerCurrent*=2; // this may result in current sensor saturation! watch out!  
    absMaxChargerPower*=2; 
  #endif
  // set analog input pins
  pinMode(pin_C, INPUT);
  pinMode(pin_bV, INPUT);
  pinMode(pin_heatSinkT, INPUT);
  pinMode(pin_12Vsense, INPUT);
  pinMode(pin_mV, INPUT);
  // digital inputs
  pinMode(pin_pwrCtrlButton, INPUT);
  pinMode(pin_pwrCtrl2Button, INPUT);
  pinMode(pin_J1772, INPUT);
  pinMode(pin_BMS, INPUT);
  // set output digital pins
  pinMode(pin_PWMpulldown, OUTPUT);
  pinMode(pin_PWM, OUTPUT);
  pinMode(pin_maxC, OUTPUT);
  pinMode(pin_EOC, OUTPUT);
  pinMode(pin_fan, OUTPUT);
  pinMode(pin_inrelay, OUTPUT);
  pinMode(pin_outrelay, OUTPUT);
  pinMode(pin_110relay, OUTPUT);
 
  //=================================== finalize init of the sensors =============================
  // input current
  k_V_mC=get_k_V_C(INmC_SENSOR);
  V_o_mC=get_V_o_C(INmC_SENSOR);
  // output current
  k_V_C=get_k_V_C(OUTC_SENSOR);
  V_o_C=get_V_o_C(OUTC_SENSOR);
 
  // reset voltage dividers to account for the input resistance of ISO124
  divider_k_mV=upperR0_mV/lowerR_mV;
  divider_k_bV=upperR0_bV/lowerR_bV;
  //=============================== END finalize init of the sensors =============================
 
  //================= initialize the display ===========================================
#ifdef LCD_SPE
  *myLCD=uLCD_144_SPE(9600);
#else
  *myLCD=uLCD_144(9600);
#endif
  //================= finish display init ==============================================
 
  // check if the display started / is present
  // if not present, we will assume that the charger is controlled by serial data instead
  LCD_on=myLCD->isAlive();
  nReadSamples=nReadSamples0; // need this here (otherwise all measurements in setup are screwed)
 
  //==================================== ONE-TIME CONFIG =================================
  // check if needed to go into config 
  int forceConfig=0;
  EEPROM_readAnything(0, configuration);
  // reset configuration if the green button is pressed at charger start
  if(configuration.CC<=0 || digitalRead(pin_pwrCtrl2Button)==1) {
    forceConfig=1; // first time running the charger after assembly
    configuration.Vcal_k=1.; // prefill the calibration with unity so we don't get zero readings if calibration menu is skipped
    configuration.CV=CVs[configuration.battType]*10;
    // set the rest of the vars
    configuration.Vcal=0;
    configuration.Ccal=0;
  }
 
  int x = 0;
  const byte STATE_DONE = 0xff;
  const byte STATE_BT = 0x0;
  const byte STATE_CV = 0x1;
  const byte STATE_CELLS = 0x2;
  const byte STATE_CONFIRM = 0x3;
  const byte STATE_CAPACITY = 0x4;
  const byte STATE_CALIBRATE = 0x5; // sensitivity calibration only. zero point calibration done automatically on power-on
  state = STATE_BT;
 
  if(LCD_on) {  
    myLCD->clrScreen();
    myLCD->setOpacity(1);
  } else {
    state=STATE_DONE; // skip config altogether if no LCD
  }
```
Here is the rest of the code, which has a state machine:


```
while(state != STATE_DONE)
  {
    switch(state)
   {
     case STATE_BT:
       printClrMsg(MSG_THX, 50, 0, 0x3f, 0);
       // if config is not forced, just timeout and send to end of config. Else, wait until button press
       if(forceConfig==0) {
         forceConfig=BtnTimeout(5, 5); // -1 if no button pressed; 1 otherwise
       }
       if(forceConfig==-1) {
         state=STATE_DONE;
       } else { // forceConfig=1 here
         myLCD->clrScreen();
         printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CELLTYPE);
         configuration.battType=MenuSelector2(battTypeLen, battTypeLabel);
         state = STATE_CV;
       }
       break;
     case STATE_CV:
       printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CV);
       configuration.CV = DecimalDigitInput3(configuration.CV); 
       state = STATE_CELLS;       
       break;
     case STATE_CELLS:
       printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_NCELLS);
       configuration.nCells = DecimalDigitInput3(configuration.nCells); 
       state = STATE_CAPACITY;       
       break;
     case STATE_CAPACITY:
       printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CAPACITY);
       configuration.AH = DecimalDigitInput3(configuration.AH); 
       state = STATE_CALIBRATE;       
       break;
     case STATE_CALIBRATE:
       // output current zero calibration - this assumes that there is no load on startup - this is especially important for 
       // PFCdirect units!
       outC=readC();
#ifdef NEG_CSENSE
       configuration.Ccal=-outC*k_V_C;
#else
       configuration.Ccal=outC*k_V_C;
#endif
       // prep for output voltage zero calibration
       // this will generally NOT work on PFCdirect units as there is always voltage on the output
       // to calibrate at the factory / right after build, power 12V ONLY and follow through calibration
       outV=readV();
#ifndef PFCdirect
       // no discharging for PFCdirect units
#ifndef LCD_SPE
       sprintf(str, "Discharge output (now at %dV), press BTN", int(outV));  
#else
       sprintf(str, "Discharge output\n (now at %dV), \npress BTN", int(outV));  
#endif
       myLCD->printStr(0, 0, 2, 0x1f, 0x3f, 0x00, str);
       while(!(digitalRead(pin_pwrCtrlButton) || digitalRead(pin_pwrCtrl2Button)));
       outV=readV(); // re-read after discharge
#endif
       // now actual zero cal
       if(fabs(outV)<40) { // if too far off, fault out
         // output voltage calibration
         temp=outV/divider_k_bV;
         V_o_bV+=temp; // this needs to be adjusted HERE because we are calling readV() again below for sensitivity calibration
         configuration.Vcal=temp; 
         printConstStr(0, 5, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CAL0);
         delay(1000);
       }
 
       // now calibrate voltage sensor slope
       // first, double-check we have reset to zero point
       // for PFCdirect units, this will only work if ONLY 12V is powered up, no main AC connected!
       outV=readV(); // get the readings with zero-point already calibrated
       if(fabs(outV)<3) { // should be pretty tight after zero calibration
         myLCD->clrScreen();
         printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CAL1); // this asks to connect the battery
         delay(1000); // to avoid reading same button state as in prev step
         while(1) {
           outV=readV();
           if(digitalRead(pin_pwrCtrlButton) || digitalRead(pin_pwrCtrl2Button))  break;
           if(outV>10) { // loop until battery not connected
             delay(5000); // let settle
             outV=readV(); // read settled voltage
             // calibrate
             printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CAL2);
             // calibration routine here - if actual voltage > shown, REDUCE the constant
             configuration.Vcal_k=DecimalDigitInput3(int(outV))/outV;
             break; // from while() loop
           }
         }
       }
       state = STATE_CONFIRM;
       break;
     case STATE_CONFIRM:
       myLCD->clrScreen();
       printConstStr(0, 0, 2, 0x1f, 0x3f, 0x00, MSG_LCD_CONFIRM);
       sprintf(str, "%d %s cells, %dAH", configuration.nCells, battTypeLabel[configuration.battType], configuration.AH);       
       myLCD->printStr(0, 1, 2, 0x1f, 0x3f, 0x00, str);
       x=MenuSelector2(menuNavigateLen, menuNavigate);
       if(x == 0) state = STATE_DONE;
       if(x == 1) state = STATE_BT;
       break;
     default: break;
   } 
  }
  // parameters calculated from config variables go here
  // adjust core sensor constants
  V_o_bV=V_o_bV0+configuration.Vcal;
  V_o_C+=configuration.Ccal;
  divider_k_bV*=configuration.Vcal_k; 
 
  minBattV=minBattVs[configuration.battType]*configuration.nCells/10;
}
```
We are now at line 797, about halfway through to the last line 1823. Note that the voltage calibration calls the DecimalDigitInput3() function, which allows entry of the actual output voltage to obtain a ratio. Although this is perhaps clever, I found it difficult to use, and it relies on the correct value being in EEPROM. It is probably better to use a trimpot for things like this...

A better means of implementing the adjustment using the two-button approach might be to start with a function that raises the number by one count per SET button press and then increments perhaps 5 times per second when held, and then by ten counts at a time after two seconds. The next click of the SELECT button would be decrement mode so you can easily adjust back to your desired value with a few more clicks. It might also roll over at some maximum value or zero depending on direction.


----------



## PStechPaul (May 1, 2012)

The V12 firmware uses the Timer1.cpp module, which is as follows:


```
/*
 *  Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328
 *  Original code by Jesse Tane for [URL]http://labs.ideo.com[/URL] August 2008
 *  Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
 *  Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop
 *
 *  This is free software. You can redistribute it and/or modify it under
 *  the terms of Creative Commons Attribution 3.0 United States License. 
 *  To view a copy of this license, visit [URL]http://creativecommons.org/licenses/by/3.0/us/[/URL] 
 *  or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
 *
 */
#include "TimerOne.h"
TimerOne Timer1;              // preinstatiate
ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  Timer1.isrCallback();
}
void TimerOne::initialize(long microseconds)
{
  TCCR1A = 0;                 // clear control register A 
  TCCR1B = _BV(WGM13);        // set mode as phase and frequency correct pwm, stop the timer
  setPeriod(microseconds);
}
void TimerOne::setPeriod(long microseconds)
{
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
  ICR1 = pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                                     // reset clock select register
}
void TimerOne::setPwmDuty(char pin, int duty)
{
  unsigned long dutyCycle = pwmPeriod;
  dutyCycle *= duty;
  dutyCycle >>= 10;
  if(pin == 1 || pin == 9)       OCR1A = dutyCycle;
  else if(pin == 2 || pin == 10) OCR1B = dutyCycle;
}
void TimerOne::pwm(char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(microseconds);
  if(pin == 1 || pin == 9) {
    DDRB |= _BV(PORTB1);                                   // sets data direction register for pwm output pin
    TCCR1A |= _BV(COM1A1);                                 // activates the output pin
  }
  else if(pin == 2 || pin == 10) {
    DDRB |= _BV(PORTB2);
    TCCR1A |= _BV(COM1B1);
  }
  setPwmDuty(pin, duty);
  start();
}
void TimerOne::disablePwm(char pin)
{
  if(pin == 1 || pin == 9)       TCCR1A &= ~_BV(COM1A1);   // clear the bit that enables pwm on PB1
  else if(pin == 2 || pin == 10) TCCR1A &= ~_BV(COM1B1);   // clear the bit that enables pwm on PB2
}
void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
  if(microseconds > 0) setPeriod(microseconds);
  isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1);                                     // sets the timer overflow interrupt enable bit
  sei();                                                   // ensures that interrupts are globally enabled
  start();
}
void TimerOne::detachInterrupt()
{
  TIMSK1 &= ~_BV(TOIE1);                                   // clears the timer overflow interrupt enable bit 
}
void TimerOne::start()
{
  TCCR1B |= clockSelectBits;
}
void TimerOne::stop()
{
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));          // clears all clock selects bits
}
void TimerOne::restart()
{
  TCNT1 = 0;
}
```
But it does not seem to attach the interrupt, and its calls do not have the right number of arguments:


```
981:              // initialize timer here - this way will reset every time when returning from no-mains break
            Timer1.initialize(period); 
            Timer1.pwm(pin_PWM, 0);           
            Timer1.pwm(pin_maxC, 0); 
 
1181:    Timer1.setPwmDuty(pin_PWM, duty);
 
1281:    //-------------- MAIN DUTY CYCLE MANAGEMENT LOGIC ----------------------------
    // safety - fast-acting protection - zero out duty immediately
    if(outV>maxOutV*1.05) { // 5% on top of CV point
      duty=0;
      Timer1.setPwmDuty(pin_PWM, 0);
    }
```


----------



## PStechPaul (May 1, 2012)

Ver 14 firmware is much different:


```
791:  Timer1.initialize(period); 
  Timer1.pwm(pin_PWM, 0); // need this here to enable interrupt
  Timer1.pwm(pin_maxC, 0); // need this here to enable interrupt
  Timer1.attachInterrupt(&sampleInterrupt); // attach our main ADC / PID interrupt
  delay(50); // allow interrupts to fill in all analog values 
 
535://---------------------------------------------------------------------------------------------------------
// as of V13, completely new way to control the charger! proper PID loop and interrupt-based fast ADC
// this was originally driven by the need to meet requirements from the Leaf CHAdeMO protocol
//---------------------------------------------------------------------------------------------------------
// all these have to be ints or very unpleasant wrapping will occur in PID loop 
// having these unsigned has cost EmotorWerks over $1,000 in parts during testing ;-)
int targetC_ADC=0; // this is an ADC reference point for our output current
int outC_ADC_0=0, outC_ADC=0, outV_ADC=0, outmV_ADC=0, T_ADC=0, T2_ADC=0;
float outC_ADC_f=0;
// ADC interrput handler
// this is always aligned with Timer1 interrupt
// ADC conversions are always done at 4kHz frequency (or every 250uS) so by the next Timer1 interrupt, 
// we should ALWAYS have the result! 
ISR(ADC_vect) { // Analog->Digital Conversion Complete
  byte ul, uh;
  cli(); // disable interrupt until function exit. otherwise nested interrupts...
  ul=ADCL;
  uh=ADCH;
  sei();
 
  unsigned int val= (uh << 8 | ul); // assuming ADLAR=0
 
  // just load things into the variables and exit interrupt - processing all in main loop
  // for most variable, average 2 values offset 180 degrees wrt haversine wave
  // for current measurement, average 16 measurements over 8ms, or one full haversine period
  switch(ADMUX & B00000111) {
   case pin_C: { // this is measured at 2kHz frequency
     if(outC_ADC==0) {
       outC_ADC_f=outC_ADC=val;
     } else {
       // 16 cycles is 8ms here or a full haversine period
       outC_ADC_f=(outC_ADC_f*15+val)/16; // this emulates an RC filter with time constant of ~half of averaged periods
       outC_ADC=int(outC_ADC_f);
     }
     break;
   }
   // rest of vars measured at 250Hz
   case pin_bV: {
     if(outV_ADC==0) {
       outV_ADC=val;
     } else {
       outV_ADC=(outV_ADC+val)/2;
     }
     break;
   }
   case pin_mV: {
     if(outmV_ADC==0) {
       outmV_ADC=val;
     } else {
       outmV_ADC=(outmV_ADC+val)/2;
     }
     break;
   }
   case pin_heatSinkT: {
     if(T_ADC==0) {
       T_ADC=val;
     } else {
       T_ADC=(T_ADC+val)/2;
     }
     break;
   }
   case pin_temp2: {
     if(T2_ADC==0) {
       T2_ADC=val;
     } else {
       T2_ADC=(T2_ADC+val)/2;
     }
     break;
   }
   default: break;
  }
} // end ADC interrupt
```
And the "Interrupt Magic" as described by Valery:


```
//---------------- interrupt magic to initiate ADC and calc PID loop ---------------------------
// PID loop setup - see [URL]http://en.wikipedia.org/wiki/PID_controller[/URL] for some definitions
// using only PI part of it here
// parameter approximations ------------
// all constants below are effectively in 0.0001 units for the formula
// at 10-bit duty counter, 250Hz loop speed and 1000 unit range
// Example: 50A target ramp from zero, measured with a 50A bidir sensor: error is ~300
//          ramp rate is pids_Kp * 8 duty pts / sec (pids_Kp * 300 / 10000 duty points in one cycle (~4ms))
// for CHAdeMO unit with 50A max C, ramp to 50A in 2 seconds requires pids_Kp>60 (assuming full duty sweep would be required)
// OTOH, typical single-stage charger's stiffness is 10-20A per 10 duty points 
// so we don't want to be making changes of more than 10 duty points per cycle
// which corresponds to pids_Kp<330
// so the meaningfull range is probably between 50 and 300
// our motor controller has Kp=3200, Ki=30, Kd=0 
//----------------------- tuning charger PID:
// Zieglerâ€“Nichols method: the Ki and Kd gains are first set to zero. 
// The P gain is increased until it reaches the ultimate gain, Ku, at which the output of the loop 
// starts to oscillate. Ku and the oscillation period Pu are used to set the gains as shown:
// Control Type Kp Ki         Kd
//    P     0.50Ku -         -
//    PI         0.45Ku 1.2Kp / Pu -
//    PID         0.60Ku 2Kp / Pu KpPu / 8
// for this charger:
// on 330V pack (LiFePo4, milli-ohm total IR), at Kp=1000, see oscillations at Hz) - hence 
// setting Kp=, Ki=
const long pids_Kp_SLOW=60; // revert to slow PID once the current shows up
const long pids_Kp_FAST=300; // fast PID to start with
long pids_Kp=0; 
const long pids_Ki=1; // need small integral term - otherwise we get some constant offset error
const long pids_Kd=0; // for now, just PI loop
long pids_err=0, pids_perr=0, pids_i=0, pids_d=0; // all have to be signed longs in order to not screw up pid calcs
long deltaDuty=0, milliduty=0; // have to be signed
byte tickerPWM=0; // short counter used only to skip cycles
byte tickerPWM1=0; // counter counting unskipped cycles - ok to overwrap
// called on overflow of Timer1 - called every 'period' uS (20 kHz by default)
// overflow with TimerOne library means we are in the center of the PWM cycle (TimerOne is phase correct)
void sampleInterrupt() {
  // trigger actual work only on every Nth period
  tickerPWM++;
  
  // prescale is defined based on period to make constant frequency of work below
  if(tickerPWM < MEASFREQPWMPRESCALE) return; 
  // prescaler is calculated at startup so that we always end up with ~4kHz frequency here
  // therefore, every ADC conversion has 250 microseconds - which should be ok given that ADC on ATMega328P takes 100us
  tickerPWM=0;
  tickerPWM1++; // this counts at lower frequency
    
  ADMUX &= B11111000; // reset the channel to zero
  // then current is measured every second cycle - or at ~2kHz frequency
  if(tickerPWM1 & 0x1) {
     ADMUX |= pin_C;
     ADCSRA |= B11000000; // manually trigger next one
  } else {
    // Every parameter is measured every 16 cycles => 250 Hz measurement frequency for every variable
    // PID loop runs at the same frequency, as well    
    switch(tickerPWM1/2 & 0x7) {  
       // case set below is MISSING 0,4,7 - available for other sensors
       case 0: {
         // average outC
         if(fabs(outC)<1.) outC=readC(); // 
         outC=(outC*float(AVGCycles-1)+readC())/AVGCycles; 
         break;
       }
       case 1: {
         ADMUX |= pin_bV;
         ADCSRA |= B11000000; // manually trigger next one
         break;
       }
       case 2: {
         ADMUX |= pin_mV;
         ADCSRA |= B11000000; // manually trigger next one
         break;
       }
       case 3: {
         ADMUX |= pin_heatSinkT;
         ADCSRA |= B11000000; // manually trigger next one
         break;
       }
       case 4: {
         // average outV
         if(fabs(outV)<1.) outV=readV();
         outV=(outV*float(AVGCycles-1)+readV())/AVGCycles;
         break;
       }
       case 5: {
         ADMUX |= pin_temp2;
         ADCSRA |= B11000000; // manually trigger next one
         break;
       }
       case 6: {
          //====================   PID loop   ====================
          // remember that targetC is a 10-bit ADC reference point that we are trying to keep - NOT the actual current!
          pids_err = targetC_ADC - outC_ADC; 
  #ifdef NEG_CSENSE
          pids_err *= -1; // the current signal (and hence the error sign) runs in a different direction
  #endif        
          
          // if the current is non-zero already, slow down 
          // allow deviation of 5% of full range of Tamura sensor (which at 1.5V deviation produces ADC output of 300 units)
//          if(abs(outC_ADC-outC_ADC_0)>15) {
//            pids_Kp=pids_Kp_SLOW;
//            digitalWrite(pin_TEST, HIGH);
//          }
          
          deltaDuty = pids_Kp * pids_err;
      
          pids_i += pids_err;
          deltaDuty += pids_Ki * pids_i;
      
          pids_d = pids_err - pids_perr;
          pids_perr = pids_err;
          deltaDuty += pids_Kd * pids_d;
          //==================== end PID loop ====================
          
          // protect against overpowering
          if( (deltaDuty>0) && (outC > 1.1*maxOutC) ) deltaDuty=0;
          
          milliduty += deltaDuty;
          if(milliduty < 0) {
            milliduty=0;
            // stop accumulation
            pids_i=0;
          }
          if(milliduty > MAXDMILLIDUTY) {
            milliduty=MAXDMILLIDUTY;
            // stop error accumulation
            if(pids_i>0) pids_i=0;
          }
  
          // immediate protection from overvoltage - zero out duty
          // this also stops any term's accumulation before PWM_enable_ is turned on (e.g. before charger start)
          if( (PWM_enable_ == 0) || (outV > 1.05*maxOutV) ) {
            milliduty=0;
            pids_i=0; // need to stop accumulation, as well
          }
          
          Timer1.setPwmDuty(pin_PWM, milliduty/10000); 
  
          break;  
       }
       default: break;
    } // end switch
  
  } // end if(tickerPWM1 & 0x1)
  
}  // end timer interrupt
```


----------



## PStechPaul (May 1, 2012)

Here is some simple code to use the two buttons to select and set an integer value. You can click it to advance by one (sometimes two), and you can hold it to advance about 8 counts per second, so it would take about 30 seconds to get to 240:


```
const byte pin_btStep=2; // this is wired to the button (used for menu step)
const byte pin_btSelect=3; // this is wired to the button2 (used for menu select)
pinMode(pin_btStep, INPUT);
pinMode(pin_btSelect, INPUT);
int ival = 0;
char* str = "ival  ";
while (digitalRead(pin_btSelect) == LOW);
delay(50);
while (digitalRead(pin_btSelect) == HIGH);
delay(50);
while (digitalRead(pin_btSelect) == LOW) {
  Display.txt_MoveCursor(13, 7) ;
  itoa(ival, str, 10);
//  sprintf( str, "Val: %3d", ival );
  Display.putstr(str) ;
  if( digitalRead(pin_btStep) == HIGH ) {
    ival++;
    delay(50); }
  delay(50); }
delay(1000);
```
I don't know why the sprintf function did not work properly. The itoa function seems OK, but lacks the formatting of sprintf.

Here is a little video of the buttons in operation:

http://enginuitysystems.com/files/EMW/uLCD_144_SPE_Test_2079.AVI


----------



## PStechPaul (May 1, 2012)

I looked at the PID implementation of the V14 code, and I don't think it is very well done, perhaps not even really adequate. But it is a great improvement to have the ADC readings timed accurately in an ISR. The current readings, which are most time-critical, seem to be taken at a 2 kHz sampling rate, while the other readings are effectively at 1/8 that, or 250 Hz. I ran some simulations to try and determine if these times are sufficient:

```
Vin=350, PWM=8/64, Vbatt=24, Ibatt=27, t=6 msec
Vin=350, PWM=4/64, Vbatt=24, Ibatt=3.4, t= 5 mSec
Vin=350, PWM=16/64, Vbatt=125, Ibatt=7.7, t=8 mSec
Vin=350, PWM=24/64, Vbatt=125, Ibatt=16, tc=8.6 A/mSec
Vin=350, PWM=32/64, Vbatt=125, Ibat=82, t=6 mSec
Vin=350, PWM=48/64, Vbatt=125, Ibatt=236, t=6 mSec
Vin=350, PWM=48/64. Vbatt=250, Ibatt=20, t=8 mSec
```
This was no means exhaustive, but gives an idea what is involved, and how the PWM corresponds to output current under various battery loads. The period is always 64 uSec, or 15.6 kHz, and it can be seen that 6.2% PWM gives just 3.4 amps, while twice that, 12.5%, jumps the current to 27 amps. The time figures are mostly from the start of the simulation to the knee of the battery current curve where it approaches its stable value, and mostly this seems to be about 6 mSec. I also noted in one simulation that the slope of the curve was 8.6 A/mSec for a 16 amp output.

If current readings are taken every 500 uSec that should be fast enough to see an excess current of 4 amps. But look what is being done with the current readings in the ISR:


```
case pin_C: { // this is measured at 2kHz frequency
     if(outC_ADC==0) {
       outC_ADC_f=outC_ADC=val;
     } else {
       // 16 cycles is 8ms here or a full haversine period
       outC_ADC_f=(outC_ADC_f*15+val)/16; // this emulates an RC filter with time constant of ~half of averaged periods
       outC_ADC=int(outC_ADC_f);
     }
     break;
```
So, the outC_ADC_f variable will take about 8 mSec to respond to a large jump in output current. The ISR could compare the immediate reading to an absolute maximum and do a shut-down of the PWM, but that is not being done. It may be that the MaxC PWM value to the comparator will take care of this, but I don't know how well this works:


```
void setMaxC(float maxC) {
#ifdef NEG_CSENSE
  // hardware limits in case of opposite direction of the sensor
  Timer1.setPwmDuty(pin_maxC, 1023); // need something more than 3 volts as zero-current output is 2.5V...
#else
  Timer1.setPwmDuty(pin_maxC, 1024./Aref*(V_o_C+k_V_C*maxC));
#endif
}
```
The PID loop appears to be implemented on a 4 mSec schedule in the ISR as "case 6". A lot can happen in 4 mSec! Here it is in simplified form:


```
pids_err = targetC_ADC - outC_ADC;
          deltaDuty = pids_Kp * pids_err;
 
          pids_i += pids_err;
          deltaDuty += pids_Ki * pids_i;
 
          pids_d = pids_err - pids_perr;
          pids_perr = pids_err;
          deltaDuty += pids_Kd * pids_d;
          //==================== end PID loop ====================
 
          // protect against overpowering
          if( (deltaDuty>0) && (outC > 1.1*maxOutC) ) deltaDuty=0;
 
          milliduty += deltaDuty;
          if(milliduty < 0) {
            milliduty=0;
            // stop accumulation
            pids_i=0;
          }
          if(milliduty > MAXDMILLIDUTY) {
            milliduty=MAXDMILLIDUTY;
            // stop error accumulation
            if(pids_i>0) pids_i=0;
          }
 
          // immediate protection from overvoltage - zero out duty
          // this also stops any term's accumulation before PWM_enable_ is turned on (e.g. before charger start)
          if( (PWM_enable_ == 0) || (outV > 1.05*maxOutV) ) {
            milliduty=0;
            pids_i=0; // need to stop accumulation, as well
          }
 
          Timer1.setPwmDuty(pin_PWM, milliduty/10000); 
 
          break;
```
Apparently this "milliduty" variable was used to provide greater resolution for the PID loop, but then must be divided by 1000 (not a good idea in an ISR) for setting the PWM duty cycle. There is also an attempt to prevent overcurrent and overvoltage here, by setting PWM to zero, but it really should flag an error and be handled more gracefully elsewhere.

I also noticed this, in the sample interrupt routine:


```
// then current is measured every second cycle - or at ~2kHz frequency
  if(tickerPWM1 & 0x1) {
     ADMUX |= pin_C;
     ADCSRA |= B11000000; // manually trigger next one
  } else {
    // Every parameter is measured every 16 cycles => 250 Hz measurement frequency for every variable
    // PID loop runs at the same frequency, as well    
    switch(tickerPWM1/2 & 0x7) {  
       // case set below is MISSING 0,4,7 - available for other sensors
       case 0: {
         // average outC
         if(fabs(outC)<1.) outC=readC(); // 
         outC=(outC*float(AVGCycles-1)+readC())/AVGCycles; 
         break;
       }
```


```
float readC() {
  return (Aref/1024.*outC_ADC-V_o_C)/k_V_C
```
Note that it calls the readC() function twice, and uses floating point in the ISR, which is not good.


----------



## PStechPaul (May 1, 2012)

Let's analyze the operation of the circuit according to the simulation. I have added a few more data points:


```
Vin=350, PWM=4/64, Vbatt=0, Ibatt=29.5, 12 A/mSec
Vin=350, PWM=6/64, Vbatt=0, Ibatt=46, 19 A/mSec
Vin=350, PWM=4/64, Vbatt=24, Ibatt=3.4, t= 5 mSec
Vin=350, PWM=8/64, Vbatt=24, Ibatt=27, t=6 msec
Vin=350, PWM=16/64, Vbatt=125, Ibatt=7.7, t=8 mSec
Vin=350, PWM=24/64, Vbatt=125, Ibatt=16, tc=8.6 A/mSec
Vin=350, PWM=32/64, Vbatt=125, Ibat=82, t=6 mSec
Vin=350, PWM=48/64, Vbatt=125, Ibatt=236, t=6 mSec
Vin=350, PWM=48/64. Vbatt=250, Ibatt=20, t=8 mSec
```
Particularly, note the performance into a short, which will determine the safe range of PWM values for calibration, and the fastest rise time of output that an overcurrent protection circuit may need to deal with. The 350V is the worst case, but will be present for all PFC units.

Being a 50 amp charger, the PWM of 6/64, or about 10%, gives 46 amps into a short, so that would be a good starting point. My simulation shows the inductor current rising from 39A to 52A in 6 uSec, or about 2200 A/uSec. The 150 uH inductor is expected to exhibit I=(V/L)dt, or 350/150=2333 A/uSec, so that makes sense. 

Of course, the simulation assumes a perfect inductor, and the one used in this design will be subject to the effects of saturation and the resulting loss of inductance due to the constant DC current as well as the peaks. One major issue with this design is that the actual inductor current is not monitored or limited in any way. At 16 kHz, and a PWM of 10%, the peak current is only 52A, which should be well within the safe limits of the inductor, but with a shorted output, we are not transferring any power.

Now, what happens if the PWM frequency is changed to 4 kHz? The same output current is produced at the same PWM value, and the same dI/dt of 2200 A/uSec is observed, but now the current varies from 20A to a peak of 70A. This may push the inductor into saturation and cause much higher peak current than ideal theory suggests. The actual result of this will be that a lower PWM value will be needed to produce the same output current, and the current waveform during inductor charging will have a non-linear slope with a higher dI/dt toward the end of the PWM cycle, which effectively creates a higher frequency component in the current. Also, the higher peak current and time will create greater resistive heat losses in the inductor.

Another observation (via the simulation) is the rise time of the voltage applied to the inductor by the MOSFET, which shows 334V in 9 nS. Obviously, the IGBTs (and even real MOSFETs) would not exhibit such fast rise time, and the drive circuitry will also limit this, although the simulation uses a 50 nS rise and fall time. BTW, the fall time of the voltage is about 7.5 nS, or 45 kV/nS, which would be unattainable for real-world circuitry due to ever-present inductance and capacitance.

Now let's try the 4 kHz PWM with a 125V battery and 96/256 PWM (37.5%). This produces about 50A charging current. There is about a 2 mSec delay before any output current is produced, and then it rises at a rate of about 80 A/mS. It is non-linear, but at 5 mSec it is producing nearly full current. Under these conditions, the buck converter is operating in discontinuous mode, wherein the inductor releases all of its energy before the beginning of the next PWM cycle. Here is a view of the simulation:










Note that there is ringing, as shown by V(n001), during the time when the inductor no longer has current flowing through it. Also, the peak current of the inductor is now 125 amps, and very likely is well into saturation. Let's try going back to 16 kHz. At the same duty cycle, 24/64 or 37.5%, the output current is now only 14 amps. It is also running right at the edge of discontinuous mode. So we need to increase duty cycle to 29/64=45% to get 50 amps. Now it is running nicely in continuous mode with inductor current varying from 33 to 70 amps, and probably well within its "safe" limitations without serious saturation. Also notice that continuous mode eliminates the ringing:










This poses an interesting problem which may affect the software as well as the hardware. I am not going to do much hardware modification at this point, except what is easily done as a retrofit on existing kits, so we may explore what can be done in software to improve performance. It appears that using continuous mode has advantages, but it may work only at higher power levels. However, we may explore the use of "burst mode" to obtain the "best of both worlds" at lower power.


----------



## PStechPaul (May 1, 2012)

Here is a simulation of burst mode, where I modulate the drive of the MOSFET with a 200/300 uSec burst. I have also added a 100 ohm resistor and 5 nF capacitor which changes the rise time to 100 nS and fall time to 450 nS. There is also a 50 ohm resistor and 10 nF capacitor which reduce the ringing at the expense of 12 watts of losses. The MOSFET now consumes about 33 watts compared to the 24 watts or so previously. Of course, the real-world IGBTs will consume much more. The output is about 22.5 amps into a 125V battery, which is 3000 watts, so the additional losses do not affect efficiency very much. The frequency of the ringing is about 125 kHz. A snubber of 50 ohms and 22 nF reduces the ringing to pretty much a single transition with a peak of 200V, and a frequency of 86 kHz, but at the cost of about 30 watts in the snubber. The disadvantage of burst mode is that it causes much higher output current ripple, in this case varying from 20 amps to 27 amps.










Actually, a 50 ohm and 5 nF snubber seems to tame the oscillations pretty well with only about 6 watts of losses, so that discontinuous mode may not be such a problem for low duty cycle. I did the simulation without burst mode, with duty cycle 16/64 (25%), and got about 10 amps battery current with 800 mA ripple. The oscillations peak at 220V and are about 170 kHz. The inductor peak current is 27 amps, so that is not a problem at low current, where discontinuous mode may be used. Changing duty cycle to 15/64 (23.4%) changes output current to 9 amps. 10/64 (15.6%) provides 5 amps, with a delay of about 12 mSec before any current is observed. 4/64 (6.2%) gives 1.25 amps, with a delay of 45 mSec. These time delays must be considered when formulating a PID loop. It may be useful to determine the dynamic response based on the input voltage, output voltage, and output current.


----------



## PStechPaul (May 1, 2012)

I have made some progress on a "Simple Charger" sketch to operate the charger in a simple manner. Here is the entire code (except for the main loop) so far:


```
/*******************************************************************
Simple Charger
This intends to use the V12/13 hardware of the EMW charger and provide
a simple means of providing a basic charging function. The operation
will be as follows:
 
1. Initialize the display. Only the uLCD_144_SPE will be used. The Goldelox
library will be used in place of the EMW modules
 
2. Show the P S Technology logo for 5 seconds. Any button pressed will switch to
display and edit of setup parameters
 
3. After timeout or editing, the display will show the parameters being used. 
There will also be selections to RUN, STOP, and EDIT.
 
4. The edit routine will show, in sequence, the following parameters:
 
a. Input voltage. Select this to calibrate to a known voltage. Reading will
change in 10V increments from 60 to 300 when STEP button is clicked. Press
SET to store the value and assign it to the internal reading
 
b. Output voltage. Select this to calibrate to a known voltage. Reading will
change in 1V increments from 12 to 300 when STEP button is clicked. Press
SET to store the value and assign it to the internal reading
 
c. Output current. Select this to calibrate to a known output current. Reading will
change in 0.1A increments from 1.0 to 60.0 when STEP button is clicked. Press
SET to store the value and assign it to the internal reading
 
d. Battery voltage: Select this to set the nominal battery pack voltage, based on 3.4
volts per cell. Thus a 36 cell pack will be 122.4 volts. Reading will change in 0.1V 
increments from 3.0 to 99.0, and 1V increments from 100 to 340
 
e. Battery current: Select this to set the peak charging current. Reading will change 
in 0.5A increments from 0.5 to 50.0A. This will normally be set to the the 1.0C value 
of the pack. Battery current will automatically throttle down to 1/20 C when voltage
reaches 3.2 VPC.
 
f. Charge time: Select this to set the maximum charge time in minutes. Reading will
change in 1 minute increments from 0 to 60 and 10 minute increments from 60 to 600 (10 hours).
 
The SET button will advance one increment for each click, and will then increment at 10 
increments per second when held. The readings will wrap back to the lowest value at maximum.
It is best to advance quickly until you are about 10 increments from desired value, and then
use individual clicks to reach desired value.
 
5. The RUN selection will require confirmation before starting. The menu selection will change
to STOP once output is on, and another press of the button will STOP charging. Then you must
manually SELECT the RUN command before restarting.
 
6. There will also be a RESET selection that will reset time to zero and restart from zero
current with a slow ramp-up when START is selected. Otherwise a STOP follwed by START will 
continue where it left off. There will still be a ramp-up, but faster.
********************************************************************/
//#include "Simple_Charger.h" 
#include "Goldelox_Serial_4DLib.h"
#include "Goldelox_Const4D.h"
#include <EEPROM.h>
#define DisplaySerial Serial
#define RESETLINE 4
#define PRINTDELAY 7
/**************************** pin-out constants *****************************/
//========== analog pins
const byte pin_C=0; // output current pin
const byte pin_bV=1; // output / battery voltage pin
const byte pin_heatSinkT=2; // charger heatsink temp - for thermal derating 
const byte pin_12Vsense=3; // implementing undervoltage protection
const byte pin_temp2=4; // 4 - spare prewired as temp input
const byte pin_mV=5;
const byte pin_mC=7; // will only work in V12 control boards (June 2013). needed only for full digital PFC control
//========== digital pins
// 0/1 reserved for serial comms with display etc
const byte pin_btStep=2; // this is wired to the button (used for menu step)
const byte pin_btSelect=3; // this is wired to the button2 (used for menu select)
const byte pin_inrelay=4; // precharges input caps - normally pin 4, in some units pin 6 running fan relay
const byte pin_outrelay=5; // protects from reverse polarity on traction battery, precharges output resistors
const byte pin_PWMpulldown=6; 
const byte pin_J1772=7; // J1772 pilot input. 1k is hardwired on V14+ pcbs so J1772 will power on on connect
const byte pin_fan=8; // fan control - this is pin4 in all kits shipped before Mar '2912
const byte pin_PWM=9; // main PWM pin
// max current reference voltage (using PWM) - was 6 in the V13 pcb (kits shipped before March 2012)
// now moved to pin 10 so that we can use higher PWM frequency 20kHz PWM
const byte pin_maxC=10; 
// 110/220vac relay control - for non-PFC units only
// If PFC is connected, relay would never close so can be removed
// also can be left unused / unconnected if separate 110V / 220V inputs are used 
const byte pin_110relay=11; // in kits shipped before Mar 2012, this is pin 5
const byte pin_EOC=12; // end-of-charge output (see pinout diagram) - pulled low when charge is complete
// end-of-charge input from BMS. Pull low / disconnect from positive TTL signal to activate
// (normallly will be realized via connecting NC BMS loop between this pin and EOC pin (or +5V)
const byte pin_BMS=13; 
/*****************************************************************************************/
/**************************** Function Prototype Declarations ***************************/
void Display_PStech();
int Set_Value( int ival, int xpos, int ypos, int minval, int maxval );
/*****************************************************************************************/
// Globals
Goldelox_Serial_4DLib Display(&DisplaySerial);
void setup() { 
// set analog input pins
pinMode(pin_C, INPUT);
pinMode(pin_bV, INPUT);
pinMode(pin_heatSinkT, INPUT);
pinMode(pin_12Vsense, INPUT);
pinMode(pin_mV, INPUT);
// digital inputs
pinMode(pin_btStep, INPUT);
pinMode(pin_btSelect, INPUT);
pinMode(pin_J1772, INPUT);
pinMode(pin_BMS, INPUT);
// set output digital pins
pinMode(pin_PWMpulldown, OUTPUT);
pinMode(pin_PWM, OUTPUT);
pinMode(pin_maxC, OUTPUT);
pinMode(pin_EOC, OUTPUT);
pinMode(pin_fan, OUTPUT);
pinMode(pin_inrelay, OUTPUT);
pinMode(pin_outrelay, OUTPUT);
pinMode(pin_110relay, OUTPUT);
 
//================= initialize the display ===========================================
pinMode(13,OUTPUT);
pinMode(RESETLINE, OUTPUT); // Set D4 on Arduino to Output (4D Arduino Adaptor V2 - Display Reset)
digitalWrite(RESETLINE, 1); // Reset the Display via D4
delay(100);
digitalWrite(RESETLINE, 0); // unReset the Display via D4
delay(1000);
Display.TimeLimit4D = 5000 ; // 2 second timeout on all commands
// Display.Callback4D = Callback ; // NULL ;
DisplaySerial.begin(9600) ;
Display.gfx_Cls() ;
//================= finish display init ==============================================
}
void waitSel() {
while (digitalRead(pin_btSelect) == LOW);
delay(50);
while (digitalRead(pin_btSelect) == HIGH);
delay(50);
}
```
This is 5886 bytes.

The initial screen:










And the parameter display on the last value:










And a short video:

http://enginuitysystems.com/files/EMW/Simple_Charger_2080.AVI


----------



## PStechPaul (May 1, 2012)

Here is the main loop:


```
void loop() { 
  int  xpos, ypos;
  int  Vin = 120, Vout = 60, Iout = 5, Vbatt = 48, Ibatt = 25, Tchg = 60;
  char*  str = "                \0";
 
  while(1) {
    Display_PStech();
    Display.txt_Set(PRINTDELAY, 0) ;
    waitSel();
    Display.gfx_Cls() ;
    Display.gfx_Transparency(ON) ;
    Display.txt_BGcolour(0) ;
    Display.txt_FGcolour(WHITE) ;
    xpos = 0; ypos = 0;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "INPUT V = ";
    Display.putstr(str) ;
    Vin = Set_Value( Vin, xpos+strlen(str), ypos, 50, 250 );
    xpos = 0; ypos = 1;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "OUTPUT V = ";
    Display.putstr(str) ;
    Vout = Set_Value( Vout, xpos+strlen(str), ypos, 10, 310 );
    xpos = 0; ypos = 2;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "OUTPUT I = ";
    Display.putstr(str) ;
    Iout = Set_Value( Iout, xpos+strlen(str), ypos, 1, 50 );
 
    xpos = 0; ypos = 3;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "BATTERY V = ";
    Display.putstr(str) ;
    Vbatt = Set_Value( Vbatt, xpos+strlen(str), ypos, 10, 310 );
 
    xpos = 0; ypos = 4;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "BATTERY I = ";
    Display.putstr(str) ;
    Ibatt = Set_Value( Ibatt, xpos+strlen(str), ypos, 1, 50 );
 
    xpos = 0; ypos = 5;
    Display.txt_MoveCursor(ypos, xpos) ;
    str = "CHARGE T = ";
    Display.putstr(str) ;
    Tchg = Set_Value( Tchg, xpos+strlen(str), ypos, 10, 310 );
  }
}
 
void Display_PStech()
{
  Display.gfx_Cls() ;
  Display.gfx_OutlineColour(RED) ;
  Display.gfx_RectangleFilled(3,10, 126,100, LIGHTSKYBLUE) ;
 
  Display.gfx_OutlineColour(0) ;
  Display.gfx_RectangleFilled(10,20, 15,85, DARKBLUE) ;
  Display.gfx_RectangleFilled(10,20, 60,25, DARKBLUE) ;
  Display.gfx_RectangleFilled(10,50, 60,55, DARKBLUE) ;
  Display.gfx_RectangleFilled(55,20, 60,55, DARKBLUE) ;
  Display.gfx_RectangleFilled(75,20, 120,25, DARKBLUE) ;
  Display.gfx_RectangleFilled(75,20, 80,55, DARKBLUE) ;
  Display.gfx_RectangleFilled(75,50, 120,55, DARKBLUE) ;
  Display.gfx_RectangleFilled(115,50, 120,85, DARKBLUE) ;
  Display.gfx_RectangleFilled(75,80, 120,85, DARKBLUE) ;
  Display.txt_FontID(SYSTEM) ;
  Display.txt_MoveCursor(11, 2) ;
  Display.txt_Height(1) ;
  Display.txt_Width(1) ;
  Display.txt_Xgap(0) ;
  Display.txt_Ygap(1) ;
  Display.txt_BGcolour(LIGHTSKYBLUE) ;
  Display.txt_FGcolour(DARKBLUE) ;
  Display.txt_Set(PRINTDELAY, 100) ;
  Display.txt_Underline(OFF) ;
  Display.txt_Bold(OFF) ;
  Display.putstr("Technology, Inc") ;
 
  Display.txt_MoveCursor(13, 1) ;
  Display.txt_BGcolour(0) ;
  Display.txt_FGcolour(LIME) ;
  Display.putstr("SEL to continue") ;
}
int Set_Value( int ival, int xpos, int ypos, int minval, int maxval ) 
{
  //int ival = 0;
  char* str = "ival  ";
  Display.txt_MoveCursor(ypos, xpos) ;
  itoa(ival, str, 10);
  Display.putstr("   ") ;  //clear
  Display.txt_MoveCursor(ypos, xpos) ;
  Display.putstr(str) ;
  while (digitalRead(pin_btSelect) == LOW) {
    if( digitalRead(pin_btStep) == HIGH ) {
      ival++;
      if( ival > maxval )
        ival = minval;
      Display.txt_MoveCursor(ypos, xpos) ;
      itoa(ival, str, 10);
      Display.putstr("   ") ;  //clear
      Display.txt_MoveCursor(ypos, xpos) ;
      Display.putstr(str) ;
      delay(50); }
    delay(50); }
  delay(500);
  return  ival;
}
```


----------



## PStechPaul (May 1, 2012)

I am trying to understand the PWM system of the Arduino. The basic library function uses Timer1 with a frequency of 488 Hz which is the 16 MHz clock frequency divided by the maximum prescale of 32768. It can be adjusted using the standard library function to 30.5, 122, 488, 3906 and 31250 Hz with additional divisors (although it shows 31250 rather than 32768):

```
setPwmFrequency(9, 8);  // 31250/8=3906, Timer1 pins 9 & 10, 31250 Hz, divisors 1, 8, 64, 256, 1024
```
Here are some references:
http://www.righto.com/2009/07/secrets-of-arduino-pwm.html
http://playground.arduino.cc/Code/PwmFrequency

The V12 EMW code has the following:


```
#define PWM_res 1024
#define MAXDUTY 980 // very short off pulses are bad (diode does not recover by the time IGBT turns on again - generally limit Toff to MORE than 1us)
#define period 50 // us; from Oct 10 2013, this defaults to 20kHz due to use of faster IGBTs. For kits with older IGBTs, use 60-70
 
Timer1.initialize(period); 
Timer1.pwm(pin_PWM, 0);           
Timer1.pwm(pin_maxC, 0);
```
That seems to be OK. But in the V13 and V14 versions, with interrupts based on the PWM carrier frequency, there seems to be an error:


```
const unsigned int linefreq=60; // 60Hz in the US - for best performance, set this to your country's line frequency!
// scaler from PWM frequency. There is a method to the madness here - basically we want to be sampling things at 4x the line
// frequency and then use two adjacent readings to produce an average. This cancels out most of the 120Hz ripple from the readings
// in the sampling interrupt, every variable is sampled only every 1/16th period, hence the 16 divider below
// with 50uS period, 60Hz, this will produce 10.4 which will be rounded to 10, producing 4% phase error which is fine
const int MEASFREQPWMPRESCALE=(1000/period)*1000/(linefreq*4)/16;
 
void sampleInterrupt() {
  // trigger actual work only on every Nth period
  tickerPWM++;
 
  // prescale is defined based on period to make constant frequency of work below
  if(tickerPWM < MEASFREQPWMPRESCALE) return; 
  // prescaler is calculated at startup so that we always end up with ~4kHz frequency here
  // therefore, every ADC conversion has 250 microseconds - which should be ok given that ADC on ATMega328P takes 100us
  tickerPWM=0;
  tickerPWM1++; // this counts at lower frequency
 
Timer1.setPwmDuty(pin_PWM, milliduty/10000);
```
Note that the calculation of MEASFREQPWMPRESCALE produces a value for 50 uSec (20 kHz) of 5.2, so the ADC will be sampled at about 4 kHz. Other parts of the ISR produce an effective 2 kHz sampling rate for the current, and 250 Hz for other values. It turns out that the calculation is correct, but Valery's figure of 10.4 is wrong.

The idea of sampling at an integral multiple of line frequency is sound, but it should actually be 19.2 kHz which is 320 * 60 or 384 * 50. That may be too fast for older IGBTs so I may use 9600 Hz.


----------



## PStechPaul (May 1, 2012)

Now I want to code an efficient implementation of the alternating sample algorithm that appears in what IMHO is a very clumsy chunk of code there. The requirement is for Timer1 to be used for the PWM carrier as well as for triggering the ADCs at precise intervals that are a multiple of line frequency (50 or 60 Hz). Here is my code:

```
unsigned long tickerPWM=0; // counts at 9600 Hz or 19200 Hz for 102 uSec resolution timer to 119304 hours
volatile unsigned int ADCval;
float mainsV=0, outV=0, outC=0;
int outC_ADC_0=0, outC_ADC=0, outV_ADC=0, outmV_ADC=0, T_ADC=0, T2_ADC=0;
float outC_ADC_f=0;
 
  // setup ADC
  ADMUX = B01000000;  // default to AVCC VRef, ADC Right Adjust, and ADC channel 0 (current)
  ADCSRB = B00000000; // Analog Input bank 1
  // ADC enable, ADC start, manual trigger mode, ADC interrupt enable, prescaler = 128 (3 bits in the end)
  // standard prescaler is 128 resulting in 125kHz ADC clock. 1 conversion takes 13 ADC cycles = 100uS using standard prescaler
  // 64 prescaler results in ~50uS conversion time
  ADCSRA = B11001111; 
 
ISR(ADC_vect) { // Analog->Digital Conversion Complete
  cli(); // disable interrupt until function exit. otherwise nested interrupts...
  ADCval= (ADCH << 8 | ADCL); // assuming ADLAR=0
  sei();
  switch(ADMUX & B00000111) {
   case pin_C: { // this is measured at 2kHz frequency
     if(outC_ADC==0) {
       outC_ADC_f=outC_ADC=ADCval;
     } else {
       // 16 cycles is 8ms here or a full haversine period
       outC_ADC_f=(outC_ADC_f*15+ADCval)/16; // this emulates an RC filter with time constant of ~half of averaged periods
       outC_ADC=int(outC_ADC_f);
     }
     break;
   }
   // rest of vars measured at 250Hz
   case pin_bV: {
     if(outV_ADC==0) {
       outV_ADC=ADCval;
     } else {
       outV_ADC=(outV_ADC+ADCval)/2;
     }
     break;
   }
   case pin_mV: {
     if(outmV_ADC==0) {
       outmV_ADC=ADCval;
     } else {
       outmV_ADC=(outmV_ADC+ADCval)/2;
     }
     break;
   }
   case pin_heatSinkT: {
     if(T_ADC==0) {
       T_ADC=ADCval;
     } else {
       T_ADC=(T_ADC+ADCval)/2;
     }
     break;
   }
   case pin_temp2: {
     if(T2_ADC==0) {
       T2_ADC=ADCval;
     } else {
       T2_ADC=(T2_ADC+ADCval)/2;
     }
     break;
   }
   default: break;
  }  }
void sampleInterrupt() {  // 9600 Hz
  // trigger actual work only on every Nth period
  int  ADCsel;
  tickerPWM++;    // increments at 9600 Hz = 122 uSec.
  if( tickerPWM&0x01 ) {// (Change to 0x11 for 19.6 kHz)
    ADCsel = ( (tickerPWM>>1) & 0x01111); // (Change to 2 for 19.6 kHz)
    ADMUX &= B11111000; // reset the channel to zero
    // then current is measured every second cycle - or at ~2kHz frequency
    if( !(ADCsel&0x01) )
      ADMUX |= pin_C;
    else if( (ADCsel>>1) > 0 )
      ADMUX |= (ADCsel>>1);  // Values from 1 to 7
    ADCSRA |= B11000000; // manually trigger next one
    } // ADMUX will be the value of the pin being measured: 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7
  }
```
Oh, and here is my user interface for operating the charger. It needs a bit of polishing, but it's functional. You click the Green SEL button to cycle through the selections, and then the red STEP button to choose INIT, STOP, or RESET. When INIT is pressed, the progress bar appears and moves, and the readings of output voltage and current change.










And a short video:
http://enginuitysystems.com/files/EMW/Simple_Charger_2085.AVI


----------



## jackbauer (Jan 12, 2008)

Nice work!


----------



## PStechPaul (May 1, 2012)

Thanks! It's been quite a challenge, but then, that's what I live for (mostly). I have been wrestling with the code to perform the ADC readings, and I finally got it to work by copying what was done in the V14 code, but I'm not sure why my version would not work:


```
//  byte ul, uh;
  cli(); // disable interrupt until function exit. otherwise nested interrupts...
//  ul=ADCL;
//  uh=ADCH;
//  sei();
 
//  unsigned int val= (uh << 8 | ul); // assuming ADLAR=0
//  cli(); // disable interrupt until function exit. otherwise nested interrupts...
//  ADCval=val; // (ADCH << 8 | ADCL); // assuming ADLAR=0
  ADCval= ((byte)ADCH << 8 | (byte)ADCL); // assuming ADLAR=0
  sei();
```
I think it may have something to do with reading the ADCH and ADCL registers, which usually will reset the interrupt flag, at least on PICs. I'll try (re)setting the flag manually and see if it works. 

It does not.  But the problem must be that the value of the registers are not being used when I access them directly. I have experienced cases where the register address was used in a function, rather than its contents. So that's why they must first be transferred to local byte variables and then manipulated. 

Here is an image of the control screen using size 2 text, which will allow much more information to be displayed clearly. The third line is just some debugging values. Maybe it can be A-h.










If you want to try this, here is the sketch:
http://enginuitysystems.com/files/EMW/Simple_Charger.ino


----------



## PStechPaul (May 1, 2012)

Based on some observations by CKidder, I think the following improvements should be made:

1. Bleeder resistors for the main capacitor bank. There is presently about 8000 uF of capacitance with a 200k bleeder resistor, so the TC from 350V to 130V is 1600 seconds (1/2 hour), and even after 2 hours (4 TC) it is about 6 volts. An easy way to implement a fast bleeder is to use something like a 300 ohm 12 watt resistor and a NC relay that opens when 12 VDC is applied. It would draw about 1 amp initially (300W) but the TC is about 2 seconds so in 10 seconds they would be safely discharged. An LED with a 20k 5W resistor in series could show the SOC of the capacitors as a warning to stay clear. Main problem is a contactor rated 350 VDC. A 20A 600VDC SSR can be obtained for about $50:
http://solidstaterelaystore.com/pe6020.aspx

2. The input precharge surge protection via the power thermistors still causes a current spike of about 300 amps and they dissipate a lot of power (perhaps 100 watts) when the charger is drawing 30-50 amps input. Another similarly priced (AC) SSR could be used with a 300 ohm resistor across the terminals for charging the capacitors. However, the 12 VDC supply would need to be connected ahead of it. The voltage across the precharge resistor would need to be monitored so the SSR would close when ready. This could be implemented with a simple optocoupler and rectifier/resistor/capacitor. Something like 20k 5 watts would provide 15 mA at 300 volts and 1 mA at 20V where the output could still easily be sensed and it would be safe to close the relay. An LED in series would show that precharge is in progress.

It may be possible to implement the discharge with a less expensive SCR or high voltage MOSFET, IGBT, or BJT. An SCR would latch on until the bleeding is complete, so that would be good and very cheap. The transistors are only a couple of dollars, so also not an issue.

The precharge limiter on the AC side could be handled with an electromechanical contactor, although they are not easily found with 12 VDC coils. However, here is one from Grainger for $17 that is DPDT with 30A 277 VAC contacts. Contacts in parallel should handle up to 50 amps.

Here is a simulation of an implementation:










If you want to play with it:
http://enginuitysystems.com/files/EMW/120Sine-320DC_Doubler_Surge_Discharge.asc


----------



## AntronX (Feb 23, 2009)

How about this cotactor for pre-charge resistor bypass: 
TYCO ELECTRONICS KILOVAC RELAY LEV100A4ANG SPST 100A 12VDC Coil
Put it right behind main bridge rectifier if these things can't run on AC. (I wonder if 120Hz unfiltered spikes would cause them to vibrate and wear out)
Then use any small power relay to close capacitor bleeder resistor upon loss of AC power.
There is cheaper relay: 40 AMP 24 VAC Double 2-Pole Definite Purpose Contactor HVAC Packard NEW C240A
Just parallel contacts for 80A capacity.


----------



## PStechPaul (May 1, 2012)

The Tyco 100A 900V DC contactor is overkill for the precharge, although it's an excellent deal at $30 each. The other DPDT relay ($10) has a 24 VAC coil, although I have driven them with 12 VDC and PWM to reduce power when closed. 

A simple $0.50 MOSFET rated 600V and 4.8A is perfectly suited to the purpose of a bleeder.
http://www.mouser.com/ProductDetail/ON-Semiconductor/NDF04N60ZH/?qs=sGAEpiMZZMshyDBzk1%2fWi7pzL3tKdUt35rRhFQQ3f70%3d

And here is a DPDT 30A 277VAC relay for about $10:
http://www.digikey.com/product-detail/en/T92S11D22-12/PB490-ND/302526
or DPST:
http://www.digikey.com/product-detail/en/T92P7D22-12/PB486-ND/365921

An SPST 30A 277VAC is only about $3:
http://www.digikey.com/product-detail/en/T9AP1D52-12/T9AP1D52-12-ND/287569

I think it would be a good idea to install a DPST or DPDT relay on the input after the 12VDC supply, to isolate the entire unit until it is manually turned on, and it could also be used as an interlock and safety disconnect if something goes wrong.

[edit] I changed the simulation to include a second optoisolator so that the 12VDC supply output can remain referenced to the control circuit ground, labeled AGND. I had to add a 100k resistor between grounds for purposes of simulation, but it's not a bad idea anyway.


----------



## PStechPaul (May 1, 2012)

In preparation for adding the tweaks and enhancements to the charger, I removed it from the enclosure:










I tried to remove the screws for the heavy conductors on the standoffs to the IGBTs, and it just rotated the standoff. I even tried holding onto the standoff with a small wrench but I could not get a good grip. I also tried to remove the wires from the inductor used for the PFC circuit, but the compression lugs just cover where it was soldered:










Note that the frame for the inductor appears to be a home-made piece of thin steel. 










I looked up the specs on the transformer, and found this:

http://www.coilws.com/index.php?main_page=index&cPath=113

Interestingly, it is rated at 220 uH and 50 amps, and costs less than $60. It is about half the size and weight of the custom-wound toroid used for the output buck converter, and it can operate to at least 300 kHz. Makes me wonder why bother with the big expensive toroid. The construction of this inductor is interesting, being a semi-planar design with a helical coil similar to a slinky. 

I'll follow up with details on how to modify the boards and add the input surge circuit and discharge circuit. I am ordering the major parts and the total cost is less than $10.


----------



## PStechPaul (May 1, 2012)

One weak point, IMHO, is the use of aluminum M/F standoffs as conductors from the PCB connections for the IGBTs, and the wiring to the inductors and output. The standoffs are 5/16" diameter with #10-32 threads, 2" long, which is not a standard size at McMaster, but similar standoffs are about $2 each. The same sizes in brass, which is really a better choice, are $5 to $8 each (for a 3/8" hex).

The reason I see a problem with aluminum is that it forms an insulating Aluminum Oxide coating very quickly, and can cause a high resistance connection prone to voltage drop and heating. This can be alleviated by using NoAlOx compound, and I would recommend using it if you take your charger apart for repair or upgrade.

I have some other ideas, however. 3/8" brass bar is less than $1.50 for a 2" piece and can be tapped on both ends and fitted with a stud to make it a M/F standoff. That's a bit labor intensive but easy enough for a hobbyist, especially with a lathe or if the bar is pre-drilled, which takes only a minute. Heck, it can also be tapped in a couple of minutes!

Square brass, with 30% greater cross-section area and thus better conductivity, is about $10/ft from McMaster, so $1.67 for a 2" piece of 3/8" square. The same size in copper is only $8/ft, so you can get maximum conductivity for only $1.33 each.

You can also get rigid copper tubing, 3/8" OD and 1/4" ID, for $8/ft, so you could use a 2-1/2" screw and achieve the desired result of raising the connection above the level of the capacitors on the board.

However, it is unclear why the standoffs are necessary. The lugs on the wires can be fairly easily bent to near a 90 degree angle, and the connection can be made right at the board with no intervening hardware. Every connection is a potential (and frequent) source of failure, especially when dissimilar metals, like steel, aluminum, copper, and tin are involved.


----------



## sexstrap (Feb 25, 2013)

PStechPaul said:


> In preparation for adding the tweaks and enhancements to the charger, I removed it from the enclosure:
> 
> 
> 
> ...


Hi Paul

Very interesting read (not that I understand all of it) especially in view of this inductor find, I have been trying to source an inductor for my latest V12 EMW build (with little success until now) in your opinion would this inductor be a good choice?

And would a single one of these handle the full 60A 12Kw load or is it limited to 50A (as it is rated at 50A)?

I will probably only run the charger at 30A anyway as I already have a complete EMW charger with the large toroid inductor so I can use this for my new 25KW traction pack charging and use the new V12 build for my smaller 10KW Traction Pack.

Anyway keep up the good work and get well soon.


----------



## sexstrap (Feb 25, 2013)

Hi Paul

Searching on the inductor model number i found this

http://www.diyelectriccar.com/forums/showthread.php/10kw-60a-diy-charger-open-source-59210p176.html

it seems that the PFC kits are (were?) supplied with two inductors one large torid (just like the one I got with my Non-PFC kit) and a smaller one as in your previous post, The post in the link above has replies suggesting that the large torid should be used as the output inductor and the smaller one for the PFC stage, Seeing as I am building non-PFC kits in your opinion could I use one of these smaller inductors as my output inductor if I keep the charge current below the 50A rated current?


----------



## PStechPaul (May 1, 2012)

Much depends on the saturation characteristics of the two inductors. The charger does not measure the peak current of either the PFC boost or the output buck, so if it does saturate, the current can go up quickly. This might be more of a problem for the PFC boost, where the IGBT essentially shorts the rectified input line voltage to common through the IGBT. There is a resistor shown on the schematic but Valery used a short piece of copper wire that might limit current to 1000 amps or more, so that doesn't help.

In the case of the buck charge current output, saturation of the inductor would cause a sharp rise of current from the main capacitor bank into the output bank and the battery pack. The hardware maxC sensor may act quickly enough to shut down the PWM before the pack gets a high current spike, but it may take some time for the software to detect this and throttle back on the output, and in the meantime these high current spikes will be going through the output capacitors.

It's probably unwise to operate either inductor beyond its rating, especially if you use the average current of the output as the determining factor. The peak current will be much higher, especially if running a low frequency PWM. For 27A output and 16 kHz, the current swings from 18 to 36 amps, so for 54 amps it would be 36 to 72 amps. At 8 kHz it would very likely be twice that, or 72 amps swing from 18 to 90 amps, for 54 amps output. Here is the simulation again so you can see what's going on:










I'm rather impressed with the specs of the CWS inductor, as it seems to drop "only" about 50% of its rated inductance value at rated current with DC present. But I think it is a gapped core, rather than the distributed gap of the powdered iron toroid, so the saturation curve might be steeper. I don't have much means to do high power testing here (safely) and what is needed is to perform a saturation test. However, it may be possible to use a 5V 200A DC power supply that I think I have somewhere, and then drive the gate of an IGBT with a variable PWM source and observe the current waveform. I plan to do that for both the toroid and the E-core inductor. 

You may need to use two or three of the 80uH 85A inductors in series to get efficient operation at 60A charge current, but they are $72 each. I'll know much more once I do more testing. Of course, another solution is to use much higher frequency, beyond the usual IGBT limit of 20-25 kHz. The inductors should be good for at least 100 kHz, and at that point the current swing will be maybe 10% of nominal, or perhaps a single 80 uH 85A inductor may suffice. There is a tricky dance that design engineers must do when dealing with real-world inductors, especially, where simulations may not show what will really happen, and where benchtop testing can get dangerously exciting when dealing with upwards of 10kW!


----------



## PStechPaul (May 1, 2012)

In an attempt to better understand the operation of the charger, I have found a non-linear inductor model which may more closely approximate what is actually going on when the charger operates in buck mode:










You can see that it starts with the expected 2 amps/uSec of the 200 uH inductor, but the slope steepens as the inductance drops due to current. The simulation shows it saturating quite sharply after 100 uSec such that current reaches several thousand amps. If this actually happens, it could cause very large currents to flow into the battery pack.

The control is tricky because it depends on the voltage difference between the input and output of the inductor, so a high voltage pack will need a longer PWM period to charge the inductor with sufficient energy. Also, the simulation here shows the performance when starting a zero current, but when the charger is operating in continuous mode there will be an average current through the inductor at all times, which lowers the inductance.

For more information on this model in LTSpice:
http://ltwiki.org/LTspiceHelp/LTspiceHelp/L_Inductor.htm

And for saturation of inductors in a SMPS such as this:
http://www.powersystemsdesign.com/power-inductors-and-peak-current-handling-capability?a=1&c=1230
http://my.ece.ucsb.edu/York/Bobsclass/194/References/Magnetics/design_tips_march07.pdf


----------



## PStechPaul (May 1, 2012)

Here is a bit better model of a saturating inductor, as shown in a simulation with a means for testing using a high current 5VDC supply:










More about this model:
http://ltwiki.org/?title=The_Arbitrary_Inductor_model

Another inductor tester:
http://www.vk2zay.net/article/200


----------



## PStechPaul (May 1, 2012)

Just a little more data. I measured both inductors with an LCR meter, with interesting results:

EMW Toroid: 45 turns on core

----- uH @ 100 Hz 0.0197 ohms
161.6 uH @ 1 kHz 0.0208 ohms
161.9 uH @ 10 kHz 0.0378 ohms

CWS Inductor 

240 uH @ 100 Hz 0.0122 ohms
240.1 uH @ 1 kHz 0.0161 ohms
238.9 uH @ 10 kHz 0.0780 ohms

The CWS inductor at first seems superior because it has higher inductance and much lower resistance at 100 Hz and 1 kHz, but the AC resistance is twice as high as that of the toroid at 10 kHz. The toroid's windings will dissipate 136 watts at 60 amps output while the CWS inductor (used for PFC) will dissipate 125 watts at just 40 amps input. This shows the advantage of using a bifilar "Litz wire" coil on the toroid, and the rapid rise of AC resistance at higher frequency from skin effect with the semi-planar helical coil of the CWS inductor. 

The skin depth is proportional to 1/sqrt(f), and at 10 kHz a 15 AWG wire is sufficient to eliminate the effect while at 16 kHz it needs 17 AWG. The PFC circuit runs on about 16 kHz so I would suspect the AC resistance of the inductor to be considerably higher and losses greater. So perhaps anything over 30 amps may dissipate well over 100 watts which is a lot for that size inductor.


----------



## kennybobby (Aug 10, 2012)

Howdy Paul,

Are you considering the impedance of the coils at frequency in your calculations? The inductive reactance is much much higher than the dc resistance and will choke the current severely.

The L/R time constant indicates to me that these coils were probably designed and intended for mains operation, i.e. 50-60 Hz.

It is a noble task that you have taken on--to try to sort out and fix these piles of parts arranged as a so-called charger. But this is a waste of your skills, abilities and time. Given enough time i'm sure that you could sort thru the mess, but the design is flawed and incomprehensible. At best you are just polishing a turd.

Get your hands on a 10 kW tesla charger and reverse engineer that, leave this jv stuff to rot. kb


----------



## PStechPaul (May 1, 2012)

The inductive reactance is linearly proportional to frequency but is not directly applicable to a switching supply inductor. I was measuring AC _resistance_, not impedance, and it is the real counterpart of reactance. Unlike reactance, which is purely inductive and does not consume power, resistance does, and it increases with frequency due to the skin effect.

https://en.wikipedia.org/wiki/Skin_effect
http://www.allaboutcircuits.com/textbook/alternating-current/chpt-3/more-on-the-skin-effect/
http://www.audioholics.com/audio-video-cables/skin-effect-relevance-in-speaker-cables

At very high RF frequencies, Litz Wire is used to reduce this effect, and the many individual insulated conductors are specially braided to keep the current flowing equally in all.

https://en.wikipedia.org/wiki/Litz_wire

As for the EMW charger, much of it is junk, but the major components and overall design are reasonably OK and salvageable. I am learning some things as I go, such as Arduino programming and finer points of PFC, buck/boost converters, and inductors. So it is IMHO a worthwhile endeavor even if not commercially viable, and I'm not trying to make money. 

I will leave it to others to hack the Tesla charger or come up with a similar design for DIY EVs. I have my own plans to make an isolated modular charger with individual configurable units of perhaps 2-3 kW each, which may allow someone to get a small charger for, say, $500, and add to it as needed.


----------



## PStechPaul (May 1, 2012)

I have not yet completed the inductor tester, and I think I will proceed without it. I want to go ahead with a retrofit PCB that will encompass the control and driver PCBs, maintaining compatibility with the power board, so that I might be able to use it in the two chargers I have, and make it available to others. So here is a schematic of the circuit which maintains full compatibility with the EMW software:









I want to discuss some changes I plan to make that will reduce the compatibility but hopefully enhance the reliability and performance. 

1. I will keep the PC817 mains voltage sensor, but will use 100k rather than 200k to get more current for the LED and get into a more linear region. I will also use a trimpot for calibration. An H11L1 linear opto-isolator will also be accommodated.

2. I will add a differential amplifier to accomplish the same purpose. It will also connect to the rectified mains voltage but may alternately be connected directly to the mains (through the same 200k 1-2W resistors) so that it may be possible to detect the waveform and whether it is AC or DC. The signal will be offset so the ADC of the Arduino can read it.

3. The A7520 now used for the measurement of output voltage will be included, but I question the rationale for the offset introduced by R3 in the schematic. I'll probably include it in the design but will see how it works without it.

4. I will also add a differential amplifier for this measurement. Further testing will determine the superior method.

5. I want to do a complete redesign of the PWM drive circuitry which now uses four transistors and an LM211 comparator in a rather awkward circuit. I will use a single transistor to drive the LED of the A7120 IGBT driver, which will have a pull-up to 12 VDC. I may use a comparator to implement a quick PWM shut-down on overcurrent, but it really should be a latch so that the processor would need to reset it before trying again. So instead of using an analog out on D10 for a programmable maximum current, I can use it for the latch reset, and use a trimpot to set the maximum current. I don't see any real need for it to be programmable.

6. A rather severe issue with this charger design is the lack of measurement of peak inductor current, which is also what is supplied by the IGBT. The output overcurrent shutdown does not measure the peak currents that are pumped into the output capacitors, and that is important to know. So I intend to add a small 100A Hall Effect donut CT through which the inductor lead may be inserted, and the cycle-by-cycle current may be monitored. The ADCs of the Arduino are not really fast enough at 100 uSec, so a hardware peak current PWM shutdown needs to be used. It need not latch off, but merely terminate the PWM until the current drops significantly, or until the next cycle. It should also signal the processor that the current limiting is occurring, and duty cycle should be lowered.

7. As I had discussed elsewhere, the J1772 pilot signal circuit does not seem to match the specification, and instead seems to tell the EVSE to start supplying power immediately. I will need to review this and make whatever changes seem appropriate. Not being too familiar with EVSEs, and not having one for testing, I'd appreciate any feedback you may be able to supply.

I welcome any and all comments on my proposed redesign, hopefully before I finish it and lay out the boards. I will also add the input precharge circuitry and the output capacitor discharge that I discussed previously. So now I will archive this schematic and modify it to what I plan to build.


----------



## jackbauer (Jan 12, 2008)

Great to see this charger being updated. It was an immense pain to build and to even find a schematic for the various PCB versions. Thanks for the hard work Paul I have a liquid cooled setup waiting to go so once you have some boards I can help with testing.


----------



## Williamws3 (Apr 7, 2014)

Hello Paul, I hope you are recuperating well from the surgery. Also glad to see that you're continuing to support folks on the EMW charger. (Valery's original forum is now down?) Mine is one of the ones you've got in hand to troubleshoot. No way I can follow all the technical issues but let me know what you find out and whether my charger can be made to function. Best to correspond at my email [email protected] since I don't come to this forum too often. Yrs, Wm Smith III


----------



## PStechPaul (May 1, 2012)

I was declared "completely healed" at my last evaluation 4 weeks ago and today I went in to be evaluated for surgery on my lower back, which may be performed in late August. Dr. Fedder, the surgeon, selected a couple of the films of my cervical spine surgery to show and tell at a lecture he was giving later today. Apparently I am his "poster boy" for the procedure! 

It's a bit hard to find the energy and enthusiasm sometimes to work on the charger, but I am committed to making a retrofit PCB and getting it to work. I think yours is the second one, in the wooden box, and once I get the first one working it should not take long to fix yours. I can't guarantee it will work with the EMW firmware, although I'm making it as compatible as possible. The firmware will still be for the Arduino, and open source, so it can be easily modified as needed, and I hope someone can step in with more Arduino experience and add functionality as needed. 

The EMW charger thread is still up, but not active. My post was the last: http://www.diyelectriccar.com/forums/showthread.php/10kw-60a-diy-charger-open-source-59210p286.html


----------



## jackbauer (Jan 12, 2008)

Keep up the good work Paul


----------



## cts_casemod (Aug 23, 2012)

PStechPaul said:


> 7. As I had discussed elsewhere, the J1772 pilot signal circuit does not seem to match the specification, and instead seems to tell the EVSE to start supplying power immediately. I will need to review this and make whatever changes seem appropriate. Not being too familiar with EVSEs, and not having one for testing, I'd appreciate any feedback you may be able to supply.


Hi Paul,

I can confirm all EVSE's I've used are perfectly happy with that. As long as it passes the diode check, is asked to start supplying power and it does not go over the EVSE power limit things run smoothly. 

One thing to have in mind is if there is circuitry to stop the pilot and disengage the cable. Here the cable is locked until the acess card is present (at which time it cuts power down also), not sure how it works on other places tough.


----------



## PStechPaul (May 1, 2012)

I have been doing some work on using a possibly better IDE (Integrated Development Environment) for the Arduino sketch. I've tried Atmel Studio 6.2 and Atmel Studio 7.0, which can use the AVR Dragon to read and write to the flash memory of the Arduino core processor ATmega328P used in the Arduino Pro Mini, but I'm also using a "Visual Micro" extension for compatibility with the Arduino sketch and environment, and I had a number of problems. I spent a fair amount of time on their forum which helped some, but I had posted my experiences extensively and the moderator tried to put them all together but instead seemingly deleted the threads or made them unavailable. 

http://www.visualmicro.com/forums/YaBB.pl?num=1459804993

I was already prepped for lumbar spine surgery on Friday but they found an abnormality in my EKG and canceled it until I can have further tests to determine if my heart is healthy enough for the considerable stress of this surgery. So it will be probably a month or more before I can get that resolved. Thus I will be able to do more work on this project, and I now have set up a separate desktop computer with XP Pro and my PCB design software, which wouldn't run on my Win8 machine I am now using. 

I need to go back through this thread and refresh my memory on the issues with the existing EMW hardware and the improvements I intend to make. Then I can design a first prototype of a replacement PCB that will be as compatible as possible with the main power board and IGBTs, as well as the Arduino Pro Mini and the uLCD-144-G2 display. I'll outline the proposed new design in a forthcoming post, but in the meantime, please let me know if you have any suggestions so I might incorporate them. This will probably take a couple of iterations, with an initial prototype designed for maximum flexibility and ease of troubleshooting, followed by a more or less final design that may be suitable for general upgrade. 

It may help if you can take pictures of your chargers, especially the PCBs, so I can see the various versions and modifications that have been done. Also perhaps provide the firmware you are using, and any quirks or bugs that you might notice. I intend to provide a totally new interface, with very few custom versions, and that should greatly simplify the project design and future updates and maintenance. 

Thanks!


----------



## PStechPaul (May 1, 2012)

I have started on the PCB design for a retrofit circuit for the charger. I made a decal for the uLCD-144-G2 display module, which has a dual row 10 pin header. But the older module uLCD-144 has a 9-pin straight header, which seems to be more standard among similar displays. The pinouts are as follows


```
[FONT=Courier New]uLCD-144-G2
1. +5V in     2. +5V out
3. TX         4. IO2
5. RX         6. IO1
7. GND        8. GND
9. RESET     10. 3.3V out

uLCD-144
1. 3.3V out
2. IO2
3. GND
4. IO1
5. RESET
6. GND
7. RX
8. TX
9. 5V in

Nokia 5110
1. LED
2. SCK
3. SDA
4. A0
5. RESET
6. CS
7. GND
8. VCC[/FONT]
```
Obviously, the Nokia and many other similar displays use an SPI interface. The one I bought awhile ago is a 128x160 display with the following pinout:


```
[FONT=Courier New]1. LED
2. SCK
3. SDA
4. A0
5. RESET
6. CS
7. GND
8. VCC
[/FONT]
```
That appears to be identical and I was able to run a demo using my Arduino UNO. These more "generic" displays use the Adafruit libraries rather than the Goldelox for the uLCD-144, and they are less than $5, while the displays from SparkFun are $35. It may be better to rewrite the code for the SPI interface, and that will also free up the RX and TX of the Arduino for serial communication and Bluetooth. Here is a rather bad video of the demo:


----------



## Tesseract (Sep 27, 2008)

PStechPaul said:


> ...https://www.youtube.com/watch?v=boagCpb6DgY
> I'm also going to order a few displays from Banggood, some of which are twice as large and have touchscreen, which will eliminate the quirky pushbuttons: ...


Be warned that capacitive touchscreens (the most common type in consumer products these days) behave erratically in the presence of high dV/dt electrical noise.


----------



## PStechPaul (May 1, 2012)

Yes, I had not considered that, and I plan to keep the two pushbuttons for compatibility. AFAIK, the display and human interface are mostly used for initial setup and monitoring the progression of charging. The touchscreen could be disabled while the charger is running, or while it is in the car while driving, where EMI may be more of a problem.

This is intended to be a way to improve the reliability and performance of the EMW charger, using as much of the existing hardware as possible. Most of the problems seem to stem from the control and driver PCBs, while most of the cost is in the power section - inductors, capacitors, and IGBTs - and I want to keep the same enclosure. The retrofit control/driver PCB will probably cost less than $100 as a kit, or perhaps $200 or so fully assembled. Not a bad investment for a $2000 charger.

There are basically two power board designs. The earlier ones have large 450V capacitors in parallel, and have direct connection of high voltage portions to the control PCB. The newer ones (V14) have banks of two 200V capacitors in series, connected as 2S7P, but they do not have balance resistors. The other major "improvement" is having the voltage divider for output voltage located here, which makes the battery (+) voltage protected. But the battery (-) goes through a 2.7k resistor to this connection. Since this is essentially connected to one side of the AC mains, it is still not "safe". Thus the need for a GFCI, or a future isolated design, which is beyond the scope of this retrofit.

Thanks for the "heads up".


----------



## cts_casemod (Aug 23, 2012)

PStechPaul said:


> http://www.banggood.com/2_8-Inch-TFT-LCD-Shield-Touch-Display-Module-For-Arduino-UNO-p-989697.html


Nowadays I would be looking for an ILI9341 based display instead. These are very common, available in a variety of sizes and inexpensive.

It works fully over SPI including the touch portion.

With that, I once retrofit the parallel display above to run on SPI, which uses less pins and is actually faster. Ill have a look to see if I still have that code in case someone wants to have a try


----------



## PStechPaul (May 1, 2012)

The ILI9341 displays seem to be a minimum 2.2" and 240x320, which is a nice size for future projects but too large to fit the charger as it is presently built. The 1.8" 128x160 is just a bit larger than the uLCD144 and should work.

I am a little puzzled by the SPI vs I2C (TWI) interfaces. There are various discussions of the differences, but it looks like the SPI can be used for both the display and the SD card, with SCK, MOSI, and MISO, plus RESET and a CS. The I2C uses pins A4 (SDA) and A5 (SCL). It seems that the display pin SDA can use MOSI and the SCK is the same as SCL. It also has an A0 or DC pin used for Data/Control. So I'm going to try a demo that uses the "high speed" interface and the Arduino SPI library. Oddly (IMHO) the SPI.h file has a lot of C code and the SPI.cpp file has quite a few #define lines.

The demo worked, but the Arduino compiler complained about a loop that seems fine to me:


```
cc1plus.exe: warning: iteration 1u invokes undefined behavior [-Waggressive-loop-optimizations]

C:\Users\paul_000\Documents\PICcode\LCD_Graphics_I2C\1.8SPI_v2\Arduino_UNO1.0\examples\graphicstest_highspeed\graphicstest_highspeed.ino:202:3: note: containing loop

   for(j = 0 ; j <= 4; j++) { // was t+=1

   ^
```
Here is the entire loop:


```
void testroundrects() {
  tft.fillScreen(ST7735_BLACK);
  int color = 100;
  int i;
  int j;
  for(j = 0 ; j <= 4; j++) { // was t+=1
    int x = 0;
    int y = 0;
    int w = 127;
    int h = 159;
    for(i = 0 ; i <= 24; i+=1) {
      tft.drawRoundRect(x, y, w, h, 5, color);
      x+=2;
      y+=3;
      w-=4;
      h-=6;
      color+=1100;
    }
    color+=100;
  }
}
```
Some links for this and similar displays:

https://learn.adafruit.com/1-8-tft-display/overview
http://users.ece.utexas.edu/~valvano/Volume1/1-8-tft-display.pdf
http://www.benbarbour.com/arduinolcd/
http://tronixlabs.com.au/display/lcd/tft/1-8-tft-lcd-for-arduino-with-sd-card-breakout-australia/


----------



## cts_casemod (Aug 23, 2012)

PStechPaul said:


> The ILI9341 displays seem to be a minimum 2.2" and 240x320, which is a nice size for future projects but too large to fit the charger as it is presently built. The 1.8" 128x160 is just a bit larger than the uLCD144 and should work.


In that case the ST7735 and S6D02A1 are better, yes!



PStechPaul said:


> I am a little puzzled by the SPI vs I2C (TWI) interfaces. There are various discussions of the differences, but it looks like the SPI can be used for both the display and the SD card, with SCK, MOSI, and MISO, plus RESET and a CS. The I2C uses pins A4 (SDA) and A5 (SCL).



Speed, reliability and I/O usage. 
SPI is much faster than I2C (8MHz versus 400KHz), but uses more pins (one for each slave CS). That can easily be solved with a de-multiplexer or a BCD do decimal converter (4 bit = 16 outputs)
The reset pins are not always required. For most SPI devices only a pull up to VCC (3.3V) is required.

I2C may lock up from time to time due to transmission errors, freezing the CPU. There are alternative libraries, but these require re-writing the library (#cpp) to call the new functions.
I use this one, which works as expected, doesn't lock up and can run inside interrupts, for example to backup data once power is lost.
http://dsscircuits.com/articles/86-articles/66-arduino-i2c-master-library



PStechPaul said:


> It seems that the display pin SDA can use MOSI and the SCK is the same as SCL. It also has an A0 or DC pin used for Data/Control. So I'm going to try a demo that uses the "high speed" interface and the Arduino SPI library. Oddly (IMHO) the SPI.h file has a lot of C code and the SPI.cpp file has quite a few #define lines.


For some reason the chinese like to mark MOSI as SDA and SCK as SCL on SPI devices which makes it confusing: those are not I2C compatible.



PStechPaul said:


> The demo worked, but the Arduino compiler complained about a loop that seems fine to me:
> 
> 
> ```
> ...



Looks fine.
I've modified it as below to save a few bytes of RAM, could you try with that and see if it still complains?



```
void testroundrects()
{
  tft.fillScreen(ST7735_BLACK);
  int color = 100;
  for(uint8_t i=0; i<=4; i++) 
  { 
    int x = 0;
    int y = 0;
    uint8_t w = 127;
    uint8_t h = 159;
    for(uint8_t j=0; i<=24; j++) 
    {
     tft.drawRoundRect(x, y, w, h, 5, color);
      x+=2;
      y+=3;
      w-=4;
      h-=6;
      color+=1100;
    }
    color+=100;
  }
}
```


----------



## PStechPaul (May 1, 2012)

I made those changes (although there was an "i" instead of "j" in the inner loop), and it still complains about the outer loop. I also left h and w as int, since the function is declared as:


```
fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
      int16_t radius, uint16_t color)
```
*[edit]* I changed the variable "color" to uint16_t (as it is declared in the header file) and it stopped complaining. Problem was that the loops could increment the variable past its limit as a signed integer.


```
[COLOR=Blue]  uint16_t color = 100;[/COLOR]
```
The inner loop incremented color 16 (or 17?) times by 1100 for a maximum of 18700. Then the outer loop runs 4 or 5 times so it could be as high as 93500. Maybe it's 16 * 4 * 1100 = 70400? Still overflows, but maybe that's OK for unsigned.

I added a line in the final endless loop to display the running time, and the number became garbled because it did not clear the area to be printed. I tried printing a " " but that didn't work, and I also tried printing a 0xDB which is an extended ASCII full rectangle, but that didn't seem to work either. I was able to fix it by using a FillRectangle() function, which worked, but that was klunky. Finally I found a detailed document for the library, and found that the setTextColor() function has a second optional argument that can be set to the background color, and that fixed it most elegantly:


```
void loop() {
//  tft.setTextColor(ST7735_BLACK,ST7735_BLACK);
  tft.setTextSize(2);
//  tft.setCursor(45, 72);
//  tft.print(0xDB);
//  tft.print(0xDB);
//  tft.print(0xDB);
//  tft.fillRect(45,72,36,14,ST7735_BLACK); // 5x7 =>10x14 + 2 => 12x14 * 3 = 36x14
  tft.setCursor(45, 72);
  tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
  tft.print(millis() / 1000);
  
  tft.invertDisplay(true);
  delay(500);
  tft.invertDisplay(false);
  delay(500);
  }
```
I left in some of the previous attempts for reference. The tft.function construct was confusing (and still s, somewhat), but it seems to be a way to handle various text and graphics functions generically for multiple tft display functions. The "print" and "println" prototypes are in the Arduino "print.h" file, and there are numerous sets of arguments:


```
size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);

    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);
    size_t println(const Printable&);
    size_t println(void);
```
The graphics functions are in Adafruit_ST7735.h:

```
class Adafruit_ST7735 : public Adafruit_GFX {

 public:

  Adafruit_ST7735(int8_t CS, int8_t RS, int8_t SID, int8_t SCLK, int8_t RST = -1);
  Adafruit_ST7735(int8_t CS, int8_t RS, int8_t RST = -1);

  void     initB(void),                             // for ST7735B displays
           initR(uint8_t options = INITR_GREENTAB), // for ST7735R
           setAddrWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1),
           pushColor(uint16_t color),
           fillScreen(uint16_t color),
           drawPixel(int16_t x, int16_t y, uint16_t color),
           drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color),
           drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color),
           fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
             uint16_t color),
           setRotation(uint8_t r),
           invertDisplay(boolean i);
  uint16_t Color565(uint8_t r, uint8_t g, uint8_t b);
```
Here is the documentation for the library:
https://learn.adafruit.com/downloads/pdf/adafruit-gfx-graphics-library.pdf

And here is a useful extended ASCII reference:
http://ascii-table.com/ascii-extended-pc-list.php

I might as well also add the Atmel ATmega reference:
http://www.atmel.com/images/Atmel-8...PA-168A-168PA-328-328P_datasheet_Complete.pdf


----------



## cts_casemod (Aug 23, 2012)

PStechPaul said:


> I made those changes (although there was an "i" instead of "j" in the inner loop), and it still complains about the outer loop. I also left h and w as int, since the function is declared as:


You can give them any name as long as they are initialized inside the loop. When the loops dont run one inside other (nested) the same name can be reused too.



PStechPaul said:


> *[edit]* I changed the variable "color" to uint16_t (as it is declared in the header file) and it stopped complaining. Problem was that the loops could increment the variable past its limit as a signed integer.


Welcome to the wonderful arduino world  

Since you're at it, some tips you may find useful:

Do be aware that printing takes considerably more time when using that function. I normally set the static variables inside the setup function (Ignore the references to the ILI9341 display):

```
tft.setCursor(160, 6);
tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(2);
tft.print(F("TOTAL: "));
```
and then only update the non static inside the loop

```
tft.setCursor(160, 30);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);  tft.setTextSize(1);
tft.print(tot_energy);
```
Notice the tft.print(F(...));
That saves the printing characters (total of 7 bytes for the word "total: " on the flash memory rather than the RAM. You'll find that using the color display eats trough the ram on the mega328 at speed of light. When strange things start to happen, remember that 

Another useful function is the printf argument


```
sprintf(instant_energy, "%06dWh", energy);
  tft.print(instant_energy);
```
For example, when one prints digits that change from 100 to 99 the display prints 100, 99(0), 98(0) -> the .setTextColor only clears the background for what is printing: If printing 2 characters won't only clear the background for the 3rd, so a ghost character will remain on display.

The function is useful for an array of other things, sadly the arduino version doesn't include floating point printing (15.908) but maybe you can add it.

Finally:

One can get rid of the arduino digital_write functions to control the CS port and use direct port manipulation such as 


```
PORTD &= ~_BV(5);  // Clear pin
PORTD |= _BV(5);     // Set pin
```
That increases print speed at least 2x (saves memory too)


----------



## PStechPaul (May 1, 2012)

I wrote a PIC function to display floating point on an LCD character display, but it could be easily ported to the ST7735:


```
/******************** Write float value to display ******************
1000 - 9999        No DP
100.0 - 999.9    DP2
10.00 - 99.99    DP3
0.000 - 9.999    DP4
********************************************************************/

void    LCD_WriteMsgFloat( char row, char col, char    prec, float fval )
    {
    int        ival, mult;

    if(fval < 0) {
        sprintf(DispMsg, (rom far char*)"-" );
        LCD_WriteMsg( row, col, DispMsg );
        if(prec>0)
            prec--;
        }
    fval = fabs(fval);
    mult = 1;
    for( ival=1; ival<=prec; ival++ )
        mult *= 10;
    if( fval>=0 )
        ival = floor(fval);
    else
        ival = ceil(fval);
    sprintf(DispMsg, (rom far char*)"%*d", 4-prec, ival);
    LCD_WriteMsg( row, col, DispMsg );
    if(prec>0) {
        LCD_Write_Data( '.' );
        fval = mult*(fval+0.5/mult-ival);
        ival = abval(floor(fval));
        col += (strlen(DispMsg)+1);
        sprintf(DispMsg, (rom far char*)"%0*d", prec, ival);
        LCD_WriteMsg( row, col, DispMsg );
        }
    }
```
and another:

```
void ftoa_C18(char* fpascii, float fpval, int prec)
    {
    long             lWhole, lval; // Stores digits left of decimal
    unsigned long    ulPart; // Stores digits right of decimal
    int                mult, ival;
    float            fval;

    //Convert number from float to fixed point for display.
    //The number is converted to two parts.
      mult = 1;
    for( ival=1; ival<=prec; ival++ )
        mult *= 10;
    //lWhole=(long)((float)fpval);
    
    fval = fpval;
    if( fval < 0 )
        fval = -fval;
    lWhole=(long)floor(fval);

    if(prec>0) {
//        fval = mult*(fval+(0.5/mult));
        fval = mult*fval;
        lval = lWhole;
        if(lval < 0)
          lval = -lval;
        lval = mult*lval;
        fval -= lval;
        ulPart = (unsigned long)floor(fval);
#ifdef  __XC8
                sprintf(fpascii, "%*li.%0*li", 4-prec, lWhole, prec, ulPart); }
#else
                sprintf(fpascii, (rom far char *)"%*li.%0*li", 4-prec, lWhole, prec, ulPart); }
#endif
    else
#ifdef  __XC8
        sprintf(fpascii, "%4li", lWhole);
#else
        sprintf(fpascii, (rom far char *)"%4li", lWhole);
#endif
```


----------



## PStechPaul (May 1, 2012)

I had thought that the uLCD144 also could be driven with SPI, but it appears to be strictly asynchronous serial. The Goldelox command set is similar to the Adafruit ST7735, but not identical. I had wanted to keep the uLCD144 display so it could be reused, but it is soldered in and not easily removed. The actual display area of the uLCD144 is 1.09" wide and 1.13" high, while the ST7735 1.8" display is 1.23" wide and 1.52" high. The actual displayed pixel array of the ST7735 is 1.10" wide and 1.38" high while that of the uLCD144 is 1.00" wide and 1.04" high. 

I found a few sources for the 1.44" display, with the more standard SPI interface:
(1) http://www.amazon.com/WYPH-1-44-1-44inch-LCD-Display/dp/B00RUT2YQ2 ($6.49)
(2) http://www.ebay.com/itm/1-44-Serial...-LCD-Display-Module-for-Arduino-/371405593459 (6 pin interface, $9.99)
(3) http://www.ebay.com/itm/LCD-1-44-Re...CD-Display-Module-for-Nokia-5110/171907483814 ($5.13)
(4) http://www.ebay.com/itm/1-44-Red-Se...-Display-Replace-Nokia-5110-LCD-/262136737086 ($3.31)

Except for #2 they all seem to have the same interface and form factor, and the controller is ILI9163. There is a library for that: https://github.com/sumotoy/TFT_ILI9163C. It works with the Adafruit library - that's good. Here is a tutorial: http://henrysbench.capnfatz.com/hen...ays/arduino-1-44-in-spi-tft-display-tutorial/

The #2 display is a bit different, being from "Digole", and it has its own programming specification and demo code and libraries, something called u8glib: https://github.com/olikraus/u8glib

It is a bit confusing. Many of the 128x128 displays claim to have an ST7735 controller which is compatible with 5110. I bought a display from a vendor in Atlanta, for $4.95 with shipping, and it should arrive within a week, whereas the others from China and such might be May 6 to some time in June.

Just rambling a bit - but I think I'll keep an interface to the original display as well as both the SPI displays. Any of them should work OK and cost is not really a factor. I'll also need to see how the touch-screen type works.


----------



## cts_casemod (Aug 23, 2012)

PStechPaul said:


> I had thought that the uLCD144 also could be driven with SPI, but it appears to be strictly asynchronous serial. The Goldelox command set is similar to the Adafruit ST7735, but not identical. I had wanted to keep the uLCD144 display so it could be reused, but it is soldered in and not easily removed. The actual display area of the uLCD144 is 1.09" wide and 1.13" high, while the ST7735 1.8" display is 1.23" wide and 1.52" high. The actual displayed pixel array of the ST7735 is 1.10" wide and 1.38" high while that of the uLCD144 is 1.00" wide and 1.04" high.
> 
> I found a few sources for the 1.44" display, with the more standard SPI interface:
> (1) http://www.amazon.com/WYPH-1-44-1-44inch-LCD-Display/dp/B00RUT2YQ2 ($6.49)
> ...



They're only compatible with the 5110 display in terms of pinout arrangement, that is, you could theoretically change an older 5110 type display, load new software and keep the exiting hardware unchanged.

One advantage of the asynchronous serial is that one could use a RF or Bluetooth dongle and install the display remotely, rather than on the charger itself. I Think most members would be thrilled with that possibility.

Have you heard of the Nextion project? It could be controlled using the same serial setup since it has a TTL RS-232 interface. Data from the touchscreen is also sent via serial, so that could theoretically eliminate EMI issues (there is a arduino library). One could, in practice have the charger remote controlled from home or from the dash, which should eliminate some of your concerns with the exiting hardware.

Itead studio sells these displays in a variety of sizes:
https://www.itead.cc/display/nextion.html?display_size=156

http://wiki.iteadstudio.com/Nextion_HMI_Solution

They're affordable too.


----------



## PStechPaul (May 1, 2012)

Those displays are substantially larger, and also would result in the same single-source problem that exists for the uLCD144. I am planning to use a Bluetooth module for the RS-232 already in place, and that will allow the user to observe and control the charging process from a remote device like a Windows tablet or Smartphone or similar. It can be done with a simple terminal program or a dedicated application. It might be good to design the interface using HTML, but I don't know if it can work with a serial (Bluetooth) port instead of TCP/IP for internet use. Not sure if this would work:
http://www.activexperts.com/serial-port-component/howto/html/

This might be a possibility for Android devices:
https://github.com/jimgeisler/phonegap-android-bluetooth-plugin-example

or this:
http://devblog.blackberry.com/2014/10/simple-bluetooth-profile/

https://github.com/voodootikigod/node-serialport

http://stackoverflow.com/questions/592099/javascript-interface-with-rs-232-serial-port

https://strokescribe.com/en/serial-port-javascript-binary-data-transfer.html

https://strokescribe.com/en/serial-port-download.html (free demo, $20/$60 purchase)

That seems a bit much, and the solutions seem to require an ActiveX component installation, which negates the simplicity of using HTML and browser on any web-connected device. It IS possible to install a web server on a microcomputer based device, but it may need a more powerful processor that is Ethernet capable. I can write a Windows GUI so anyone with a laptop or tablet can install and use it. Those with Apple, Android, or Linux devices will have to use a plain vanilla terminal App or write something fancier.

[edit] Might not be that hard to do with the Arduino: http://www.arduino.cc/en/Tutorial/WebServer 

Or even using WiFi for a local wireless network: http://www.arduino.cc/en/Tutorial/WiFiWebServer

A WiFi Web Server shield is only about $9: http://www.banggood.com/ESP8266-Web...ESP-13-Compatible-With-Arduino-p-1008124.html

http://www.banggood.com/ESP8266-ESP...ule-Adapter-Module-For-Arduino-p-1047323.html


----------



## dcb (Dec 5, 2009)

just a personal preference, I like the business model behind cordova more than phonegap, gap looks like one of those vendor lock-in schemes (on top of layers of vendor lock-in schemes).

but even cordova is probably overkill for most apps.

I'm currently experimenting with android studio for this sort of thing, only with the gui being done in javascript.

i.e. 

hc05 to android/pc/?? to browser (or native webkit viewer thingie).

small "native" code to do simple bridging between bluetooth and web application.
it scans for an open port on the phone/pc, and opens the browser on localhost at that port, and acts like a mini web-server that can also accept requests.

so that the bulk of the coding is standardized on javascript/webview so it can be easily ported (should work with elm327 adapters too, which is actually where I'm starting as I am sitting in my leaf typing this).

But I'm slow on this stuff, so just my high level architecture suggestion for now, pc prototype will probably be java, never had much luck sorting out serial ports in python (plus it is a moving target) and the arduino folks already sorted it for java and it should port to android ok. But I really don't want any more code than necessary in the hard-for-folks-to-sort-out-the-toolchain bits, except to make bluetooth access standard. If you can edit a few .js files on your phones sd card or on a website then that would be about ideal.

just my $0.02

Also SL4a actually kind of works like this already, but it isn't maintained anymore and all the options (polyglot) kind of confuse things, but it is still cool.

edit: there are newer forks of sl4a, but without playstore support it will be a PITA.

looking at http://droidscript.org/ (already found an HC-05 example in the IDE on my phone)


----------



## PStechPaul (May 1, 2012)

I was unfamiliar with Cordova, and I hope the link to the Apache site is what you mean. I was unable to do anything for the Android tablet I have, in terms of creating Apps. I used Android Studio and Eclipse, and I wasted hours just trying to get a "Hello, world" app to run. The simulator/emulator seemed unbearably slow, and I gave up. I have a Win8.1 tablet that was under $100 and it works well for what I want to do. I consider this to be an open source collaborative effort and I'd welcome other contributors to various aspects of the development.

My main effort now is to design a prototype PCB that will include a control/driver board as well as a power board to function as a low-power charger using the same basic configuration as the EMW units. One problem with designing circuits and debugging hardware and software is the large amount of energy stored in the capacitors (and inductors) that can create a dangerous and expensive situation if something goes awry. I want to have confidence that things are working as expected without blowing up components, before "going live" even at reduced power levels (about 2000 watts for 120 VAC and 20 A). 

There is only so much I can do with the power hardware as it is now. I plan to monitor the actual AC or DC input line voltage, the rectified DC voltage, and the intermediate DC bus voltage from the PFC section. I plan to replace the current sense "resistor" (presently a loop of wire) with a proper shunt (for the PFC overcurrent). For the actual buck current regulator, I will keep the present Hall effect current sensor used for actual battery charging current, and the actual battery voltage using a differential amplifier rather than the ISO-amps. I would also like to monitor the inductor current, and implement a fast analog PWM shut-down, so I plan to add another Hall current sensor and pass the inductor leads through it.

I think the software for the charger can be greatly simplified and made more reliable. That is where I expect to spend most of the time, once I have finished the preliminary PCB design. Since I plan to keep the Arduino Pro Mini it should be easy for customers to make modifications and improvements to suit their needs. Basically all that is needed is to set the target charge voltage, maximum current, and charging profile. There may also be preset time for charging and a few other items, but I think that's all that's needed. I plan to eliminate the software calibration and instead use some trimpots for hardware calibration.

Comments and discussion are welcome!


----------



## PStechPaul (May 1, 2012)

Here is nearly complete schematic of what I plan to do:










Here is a PDF of the schematic: http://enginuitysystems.com/files/EMW/EMW_PFC_PCB_retrofit_1.pdf

Some design points for review and discussion:

1. Mains voltage is measured using a differential amplifier with a total of 200k input resistors and 2k divider resistors, so 240 VAC will become 2.4 VAC or 7.2 V P-P. This is then shifted by using two 1k resistors to +5V so the signal to the ADC input will range from 0.7V to 4.3V with the waveform centered on 2.5V.

2. Battery voltage is similarly measured with a 100:1 differential amplifier, so 450 VDC will provide 4.5 VDC to the ADC.

3. I will replace the copper wire "shunt" with a proper 0.003 ohm 5W sensing resistor, which will provide 120 mV at 40 amps. For higher current, two in parallel could be used. This is used by the IR1153S PFC controller as previously designed. This signal also goes to a differential amplifier with a gain of 20, so the 120 mV will become 4.2 volts. This is sensed by the second half of the op-amp configured as a comparator with a trip point of about 4 volts, and it drives an optocoupler that provides overcurrent information to the processor. 

4. I have added a trimpot to the PFC so the voltage can be adjusted.

5. I added LEDs to the gates of the IGBTs to show when they are ON, and a rough indication of PWM.

6. There is an open loop Hall transducer that accepts a wire through the donut hole to sense up to +/- 100A of battery charge current. I also added another 0.003 ohm resistor and an HV7800 shunt monitor that works up to 450 volts, and eliminates the more expensive CT. It's an alternative method to be tested.

7. I made provision for the HCPL-3120 IGBT drivers in DIP package as well as SI8261 in SOIC-8. We'll see what works best. The SI8261 is $1.80 compared to $4.91 for the 3120.

8. I added an input charge current limiter using a 22 ohm 5W resistor and a 12V power relay on the AC input that closes when the voltage on the resistor drops to about 10 VDC.

9. There is an automatic discharge circuit for the power capacitor bank, using a 300 ohm 12 W resistor, which is applied when the 12 VDC supply turns off.

10. I have connections for the uLCD-144-G2 display as well as the ST7735 using SPI.

11. There is a four pin connector for a Bluetooth module on RX and TX.

12. There is an ISP port compatible with the Dragon.

13. I changed the pushbuttons so they use pull-ups for a more "normal" connection compatible with open collector inverted logic (active low).

14. The PWM signal from the Arduino is configured active low with the opto-coupler pulled high. No hardware overcurrent limit at this point, but I might add it.

15. I still need to add inputs for the heat sink thermistor, and I/O for the J1772 and the BMS.

*** Have I missed anything? Should anything be added?


----------



## arber333 (Dec 13, 2010)

PStechPaul said:


> Here is nearly complete schematic of what I plan to do:
> 
> 
> Here is a PDF of the schematic: http://enginuitysystems.com/files/EMW/EMW_PFC_PCB_retrofit_1.pdf
> ...


Do consider_

1. BMS charger low input, it lowers charger to value of preset integrer of charging current. Usefull to keep more sensitive cells happy at the end of the SOC curve

2. You should keep AC precharge AND DC output relay! I use DC relay now ($ouch$) and i can unplug mail AC cable at 10kW strain and IGBT wont feel anything. Recommended!

3. I am very interested in that display. http://www.digole.com/index.php?productID=883
It is OLED, much more UV stable and cheaper than uLCD144. Besides it uses ttl/spi/iiC interface. Could it be ported to charger software?

4. Here in EU we use 380VAC as Level 2 charge source. Can you add support for 3phase 600VDC+ input? It works for me but i dont have PFC since its useless at 3ph. You could add in code if input voltage goes over 400VDC, PFC transistor would open 100% and operate as switch. That way we would just have to use higher range components, everything else would be the same. 
Heh, even better at single phase PFC would up the voltage to 400VDC and we could use the same charger on L2 and L1 stations. Now i cant use single phase since output is smaller than my batterys 370V nominal.

For 3phase at cca 30A out i only use cca 2000uF input caps and 2200 output cap with 800V snubbers of course! Stuff works at 10kW and more!


----------



## PStechPaul (May 1, 2012)

1. I was unaware of a BMS charger LOW signal, but there are certainly a few unused I/O pins. I've tried to keep most of the pin mapping the same as the EMW design:

D2, D3: Pushbuttons
D4, D5: Unused (Use for EOC out and BMS in)
D6: Shutdown (might not be needed - use for BMS Low?)
D7: J1772
D8: Fan (might not be needed)
D9: PWM
D10: MaxC out PWM (might not be needed)
D11: MISO (used for Doubler relay might not be needed)
D12: MOSI (was used for EOC output)
D13: SCK (was used for BMS input)
A0: Csen (charge current)
A1: Vsen (battery voltage)
A2: SinkT (heat sink temperature)
A3: UVlo (might not be needed, change to MainsV)
A4: SDA (T2 - unused temperature sense)
A5: SCL (used for MainsV in EMW design)
A6, A7: Unused

2. There is no DC output relay in the EMW design, but there is an optional output diode that I will include. Since the bleeder circuit will dissipate the charge on the output capacitors, there should be no harm in connecting the battery when the charger is unplugged or before the charge cycle is started. And the charge will be inhibited if no battery voltage is detected. Disconnecting the input will drop the 12V supply so the precharge resistor will be present when the charger is once again plugged in.

3. The Digole display could be used for the charger, but I don't plan to make special accommodation for it. It should be easy enough to wire it to the control PCB and make changes in the firmware. It uses a different library and perhaps a different set of function calls. That will be a DIY option. 

4. Three phase and 600 VDC input is beyond the scope of this project. I have no way of testing it and it is above the nominal 300-400 VDC range of the existing power boards. I'm not sure how the PFC circuit will work with DC input, especially if it is below the 300-400 VDC provided by the boost converter. It might work just fine as a DC-DC boost.

5. I also question the need for the 15x560 uF DC bus capacitors. The 2x560 uF on the output is probably reasonable and sufficient. I'm not sure what the ripple output is, or needs to be, for battery charging. It may even be beneficial to have some ripple. 

Thanks for the input. I think I just need to create a few PCB decals for new parts, and I'll be ready to start laying out the PCB. I plan to have one part of the PCB as essentially a replacement for the EMW control and driver boards, another similar size PCB for the precharge and discharge circuit, and a third, larger, PCB for the power board. The control/driver PCB will be 4.75"" x 3.75", same for the precharge/discharge, and the power board will be 7.5" x 3.75". So the entire board will be 7.5" x 8.5", and capable of being cut into three separate boards. The EMW power board is 8" x 3.75". So I should be able to make it mostly compatible to replace all three boards.


----------



## arber333 (Dec 13, 2010)

PStechPaul said:


> 1. I was unaware of a BMS charger LOW signal, but there are certainly a few unused I/O pins. I've tried to keep most of the pin mapping the same as the EMW design:


Yes i used the 110relay pin that is now totaly forgotten and FAN pin that is unused, since i use 230Vac fan. I made a loop, the same as with BMS/EOC and when BMS breaks the loop charger lowers amps.


D4, D5: Unused (Use for EOC out and BMS in)
D6: Shutdown (might not be needed - use for BMS Low?) *Hm, i am not sure why Val used this port with 2222 transistor...*
D7: J1772
D8: Fan (might not be needed) *Yes i use 230Vac fan*
D9: PWM
D10: MaxC out PWM (might not be needed)
D11: MISO (used for Doubler relay might not be needed)
D12: MOSI (was used for EOC output) *How do you plan to connect BMS/EOC loop now?*
D13: SCK (was used for BMS input)
A0: Csen (charge current)
A1: Vsen (battery voltage)
A2: SinkT (heat sink temperature)
A3: UVlo (might not be needed, change to MainsV)
A4: SDA (T2 - unused temperature sense)*I would use this... or at least make preparations. *
A5: SCL (used for MainsV in EMW design)
A6, A7: Unused



> 4. Three phase and 600 VDC input is beyond the scope of this project. I have no way of testing it and it is above the nominal 300-400 VDC range of the existing power boards. I'm not sure how the PFC circuit will work with DC input, especially if it is below the 300-400 VDC provided by the boost converter. It might work just fine as a DC-DC boost.


I did not mean to charge with DC. My idea was to put a possibility of 3phase charging in software, so if more than 400V would be connected to input (3ph is cca 600V rectified) PCF would just open transistor and let the buck side work it down. 

I hope to see your design working soon. I am always surprised how functional your products are.


----------



## PStechPaul (May 1, 2012)

1. I received the 128x128 display today (on my birthday) and I connected it to the same pins with the same sketch as used for the 128x160 display, and it worked perfectly well except for the limits of the display size. So that will simplify the firmware and the hardware.

2. A high signal on D6 from the Arduino will pull the PWM low and shut it down. The other 2222 transistor (S3) does the same although it just forces the PWM low from the comparator U9 when Csense exceeds maxC (D10). Maybe it's a fail-safe if the LM211 fails. 

3. The fan control was supposed to use PWM to adjust speed. But that seems unnecessary for a 10kW charger to perhaps save a few watts on the fans.

4. I need to use D12 for MOSI, mostly for the Dragon, and in case the SDI memory card is used. Also maybe for the touchscreen. 

5. I will use D4 for EOC out, and D5 for BMS in. AFAIK that's all that is needed. The charger can choose to lower the charge rate or shut off completely. I'm not sure if the BMS signal will change back at the 1/20 charge rate? If so, the charger can continue the low level charge until it trips again, and then shut down completely. The EOC out is probably used for an external relay to disconnect the battery pack. Is that correct? The EMW schematic shows these connected directly to the Arduino, but I think they should be opto-isolated, or at least protected and filtered with an RC input, and perhaps an OC output transistor or MOSFET capable of driving a 12V relay directly.

6. I may use A6 and A7 for spare thermistor inputs, or other analog inputs. I think they are not even connected in the EMW design. They don't even exist on one of the three Arduino Pro Minis I have.

7. The power board I am building, and the one from EMW, have only full wave single phase bridge rectifiers. For three phase, an external bridge would be needed, and in fact the EMW design uses a separate board with two 50A 600V bridge rectifiers in parallel, bolted to the heatsink. I will use a smaller bridge for my board, since it is designed to be only about 2kW or 20 amps at 120 VAC or 10 amps at 240 VAC. The 380 VAC 3 phase rectified to 600 VDC could be done with a custom power PCB and 1200V IGBTs and some modification to the control/driver board.

Thanks for your comments and suggestions. I have done a preliminary layout for parts placement and I need to make a few changes and new component decals, but it's getting close to routing and fabrication.

Please comment on the points above, as needed.


----------



## PStechPaul (May 1, 2012)

More progress. Here is the PCB schematic now:










I made some changes to the port assignments, and I added my J1772 circuit:









That may need some explanation. The Arduino uses output pin D6 to turn on U13 by going low, which also lights the GRN LED D11, or it can turn on U15 by going high, lighting the YEL LED D21. When U15 is ON, it takes R70 to -12V, because the white LED D10 has about 3V drop. This is sensed by the EVSE. Similarly, turning U13 on takes R70 to +12V. 

The EVSE provides a voltage on its output that is sensed by the Arduino A6 input. I need to review the specifications of the J1772, and some adjustment may need to be made, but this is what I had come up with when I was looking at the EMW EVSE interface for the "JuiceBox". I have no way of testing this, so please let me know your opinion. Thanks! 

[edit] Well that is wrong. It was my idea for the EVSE, and not for the charger. Here is a circuit for the J1772 handshaking:









Thanks to: By DavideAndrea at English Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=19090771

Here is my design for the EV Charger J1772 interface:









The proximity sensor just provides the specified resistances as shown in the suggested circuit. When plugged into the EVSE, the charging station should supply 12 VDC through 1k to the pilot terminal. This also turns on Q4 and lights trhe green LED D11. The charger defaults to EOC low using D4, which brings EOCOUT on the BMS connector low, and lights the white LED D10 through R79. If all is OK, the EVSE should now apply a +/- 12V 1 kHz square wave to the pilot, which is sensed by the A6 analog input. R77 and C31 form an RC filter with a TC of 66 uSec which preserves the waveform (as might be appropriate for a digital input), but a 1 uF capacitor could be used instead, so the TC would be 20 mSec and the voltage would be about 6 VDC. (I need to add a voltage divider, perhaps 3/1, by adding a 10k resistor across C31). The charger will see this change of voltage, and will take D4 and EOCOUT high (assuming that the battery pack needs to be charged), and this will turn on Q3 which adds the 1.3k resistor pull-down on the pilot, indicating to the EVSE that it is ready for power to be applied. At the end of the charge cycle, EOC will once again go low, turning on the white LED and requesting the EVSE to stop applying power.

I just realized that the charger will need to have a way to power the charger circuitry from the battery pack when the AC (or DC) charge power is disconnected. This might be done by powering the 12VDC power supply through two diodes that will draw power from the greater of the rectified DC or the battery pack. But this will work only for battery packs of 120 to 370 V, because of the 85-264 VAC input range of the power supply. The power supply also could be powered by the 350 VDC on the large capacitor bank, and would also function to discharge them. The 8000 uF bank at 350V stores 980 joules (watt-seconds). The 12 watt power supply fully loaded would run a bit less than a minute before reaching undervoltage. There might need to be a way to disconnect from the battery pack to avoid draining charge. 

Or, the 12 VDC could be alternately supplied by the vehicle 12V system, perhaps switched by the accessory switch so that the charger would only work with the key turned on. Another option would be a small 12V rechargeable battery in the charger, which would power the 12V control circuitry for a period of time after removal from the mains. For 12 hours of operation at 12 watts, would be about a 12 A-h battery.

*I could use some advice on this!*


----------



## Tony Bogs (Apr 12, 2014)

Power supply when not connected to the mains? My solution: http://www.digikey.com/product-detail/en/VIPER26HD/497-10742-5-ND/2423749. 
Just add a small high frequency transformer (app 1"x1"x1") , a few passive parts, some diodes and maybe a little bit of potting material.
Breakdown voltage 800V+ , 115 kHz, current mode controller, adjustable max drain current.

Meanwell has an industrial AC/DC ps with a high input voltage rating (750VDC if I remember correctly), 120W, Din rail, but with a limited temperature range (max 70C?), ~US$150.
Edit: model DRH-120, not max 70C temp but -20 to 60C


----------



## arber333 (Dec 13, 2010)

Why wouldyou want to have charger active when not in use - as in not connected to AC? 
I connect 3phase charger trough the night, but i use 3phase contactor to take the power off when charge is finished.
Of course 12V PSU is on as long as L1 and N are present. That means if there is a line mismatch, ie. L2 instead of L1 charger wont start since 12V isnt there. 

Today i figured how to charge my car from 230VAC... I put N line to the middle of capacitor bank and voila i got 620VDC from L1 only! I can now get cca 380VDC 5A from 10A single phase mains reliably. I could get more but I dont want to stress fuses too much . 
The trick is to have separate plug that can be disconnected from N line when charging 3phase. I dont want to cause short with 400VAC...

I will add a safety relay (interlocked pair) so this connection can be made only when L1 is present but other phases are absent.

A


----------



## PStechPaul (May 1, 2012)

My concern was mostly for use with EVSE charging stations using the J1772 protocol. In that case, the charger would be in the vehicle and connected to the battery pack (unless disconnected by means of a safety relay or cutoff switch). When the J1772-compliant plug is connected, it will only supply 12 VDC through a 1k resistor. That is not enough to power up the Arduino so that it can determine the battery pack SOC and other variables. 

The EMW charger uses a 1k resistor to ground, which is probably close enough to the specified 880 ohms indicating that the charger is ready to accept mains charging power. Maybe that is OK, and I might be overthinking this. The EVSE will probably shut down when the plug is removed, by using the proximity sensor, which is designed to disconnect before the mains power pins disconnect, but there could be a chance of arcing if the plug is removed too quickly. 








Perhaps a simple circuit could be made that would just delay the application of the 1.3k resistor that calls for mains charging power. Just a capacitor being charged by the 1k resistor from the 12V signal could turn on a transistor that applies the 1.3k to signal the EVSE to apply mains power. And then the charger should "wake up" and perform its pre-charge routine before starting the charge. The charger could even turn off the transistor upon EOC, so that the EVSE would turn off. BUT when the charger's control power dropped, the cycle would continue. However, perhaps a small SCR could be used, which would latch on, and remain so until the pilot 12V was removed.









Here is my idea:










However, the most sensitive SCR I could find has a holding current of about 1 mA, but the 100k resistor only supplies about 50 uA through R81 when the EVSE is sending a 50% duty cycle square wave. It might be possible to add a low power regulator driving a PIC on the A6 line, and it could be programmed to latch on when EOC is asserted, thus removing the 1.3k resistor and turning off charging power, while it stays alive from the 12V provided by the EVSE pilot. It could also eliminate the RC network as shown which now provides roughly 1-5 seconds delay before allowing mains power to be applied, and it could be programmed for a more precise delay. Q5 might be replaced with a small logic level MOSFET. 

The Microchip PIC10F320 draws less than 30 uA from 2.2 to 5.0V supply, so it should be capable of operating continuously on the 12V DC or square wave from the pilot, through the 1k resistor, while it presents only the equivalent of 5/30 or 667 kOhms. It is only a 50 cent part and might even simplify circuitry and reduce cost, although it adds a smidgen of complexity in programming.

This is what I came up with:










The 12 VDC pilot signal on pin4 will give about 8.5V out of the diode and 4.25V on A6 (R82 should be 10k), which is also the Vdd for the PIC. It will wait for 2 seconds, and then drive the gate of Q5 high, turning it ON, and pulling the voltage out of the diode to about 5.2 volts, and it will be a square wave or 2.6 volts. The divider would make that about 1.3 volts, which is too low for the PIC, which works down to 1.8V. So I would replace R83 with a 5V zener, so that the 12 VDC would be clamped to 5V, but when the pull-down is activated and the square wave is generated, the voltage should change to about 2.6 volts. The PIC can read its own power supply voltage by using an internal reference of 1.2V, so it will know the status of the EVSE. If the charger powers up, it will initialize and take D4 high, showing a charging state. When the charge is finished, D4 (EOC-) goes low, and pulls RA3 low, so the PIC knows that the charge is finished. It will then take RA2 low, removing the 1.3k resistor and hopefully shutting down the EVSE. The PIC will then enter a loop and remain in that state until the EVSE is disconnected and the power to the PIC is removed.

One more thing. The J1772 schematic shows a proximity connection with a 150 ohm and 330 ohm resistor to ground that is shunted by a switch in the EVSE connector. There is a 2,7k resistor to ground in the plug. In the controller, there is a 330 ohm resistor to a 5VDC source, and the junction of these resistors goes to the input of a circuit labeled PP. There does not seem to be a sensor in the EVSE for this circuit, so I don't know how it is used and if it is even needed.


----------



## Ai! (May 9, 2014)

Proximity switch is only a mechanical switch that use 2 different resistances for each state, there are no sensors or anything more complicated. It is needed by the protocol on both sides, but can be easily achieved by simple switch with 3 or 4 resistors. You already know the resistors nominals. I use this switch on the J1772 plug to power down the charger and avoid arcing, simple and easy to implement solution.


----------



## arber333 (Dec 13, 2010)

PStechPaul said:


> One more thing. The J1772 schematic shows a proximity connection with a 150 ohm and 330 ohm resistor to ground that is shunted by a switch in the EVSE connector. There is a 2,7k resistor to ground in the plug. In the controller, there is a 330 ohm resistor to a 5VDC source, and the junction of these resistors goes to the input of a circuit labeled PP. There does not seem to be a sensor in the EVSE for this circuit, so I don't know how it is used and if it is even needed.


Well i use this wiring in my plug.
https://mazdamx3ev.wordpress.com/2016/01/07/hitre-polnilnice/

I dont use other states since my charger is disconnected when charging is finished. I figured the best proximity resistor is 680R. Not all EVSE would work with 330R.
START button NC is there because of fast EVSE that require plug to be connected (sense) with button pressed. After that i can release button to start charging.

Yes you saw correctly, it is DIY mennekes plug .


----------



## PStechPaul (May 1, 2012)

I'm still not fully understanding this:









You seem show a 680 ohm resistor in the red connector from one of the two signal connectors (PP) to the center pin, which I would assume is safety (earth) ground, and this might be for the 150 and 330 ohm series (480 ohms) string shown in the schematic from Wikipedia. The switch seems to shunt the 330 ohms to make the resistance 150 ohms to ground. And this is shown as the proximity pin.

Your schematic shows a PE pin which I assume to be ground. The CP pin on the right side apparently connects to the diode and 2.7k resistor to PE and that resistor is shunted by the 1.3k when the switch is released. 

So it seems that the EVSE might not supply power with an 880 ohm (or 1k as is shown in the EMW schematic), until the button is pressed, which provides the 2.7k indicating the vehicle is present, and then when the button is released it will provide power. All of these components are in the vehicle's inlet connector housing IIUC. The charger connector seems to have just five male pins on one end (although a small sixth pin seems to be visible), and the other end has 5 large female pins arranged in a circle, which probably mates with a male inlet receptacle on the vehicle that connects to the charger. 

This is three phase (L1, L2, L3), with neutral (N), and another pin which I assume is safety ground, so there is no proximity or pilot connection to the charger. But I think this may not be typical of most US charging stations except the very fast charge types. Here is the typical US connector:










It seems that the signal pins may be labeled CP and PE, with CP being pilot and PE being ground. But elsewhere it seems that one of these is PP, for plug present or proximity detect pin. The handshaking states appear to be resistances across these terminals, but PE may be the earth ground pin.

Base status Charging status Resistance, CP-PE Resistance, R2 Voltage, CP-PE Status A Standby Open, or ∞ Ω 
+12 V Status B Vehicle detected 2740 Ω 
+9±1 V Status C Ready (charging) 882 Ω 1300 Ω +6±1 V Status D With ventilation 246 Ω 270 Ω +3±1 V Status E No power (shut off) 

0 V Status F Error 

−12 V
This arrangement seems to be used for Level 1 (120V 16A max), and level 2 (240V 32A or 80A). Fast charging seems to be DC, using a different (but compatible) connector:










The proximity detection circuit is supposed to prevent movement of the car while connected, so it would need to have a sensor in the charger or the vehicle as shown in the schematic. This would probably need to be an interlock connected to the motor controller or main battery pack relay, where the battery pack has a separate connection to the charger, with its own means of disconnect and protection.

I don't want to get too hung up on this part of the design. Apparently the EMW simple circuit seems to work (or does it?) I think I have read about some problems with it.


----------



## PStechPaul (May 1, 2012)

Somehow I can't edit the previous post, and what was supposed to be a chart of the signalling states lost formatting and is difficult to read. Here is a similar chart:










And the same OpenEVSE source shows this for proximity, which is a bit clearer and more useful:


----------



## PStechPaul (May 1, 2012)

I have basically finished the schematic and I have placed the parts on the board, ready to be routed. Here is an image of the three boards:










The bottom left is the control/driver board, which mounts on the panel of the charger. Note the XXX keepouts separating the control circuitry from the power section. The upper left is the input surge limiter and the capacitor bank discharge circuit, along with the 12V power supply. The right side is the power board, which uses the same large IGBTs as the EMW design, and uses single 560 uF capacitors and 10 uF film capacitors, plus two toroidal inductors that mount vertically on the PCB.

Here is an image of the layout in PADS:


----------



## PStechPaul (May 1, 2012)

I made some fairly major changes and corrections, and they are reflected in the updated images above. I completely redid the power board and surge/discharge boards, manually, and I touched up the autorouted portions of the control/driver board considerably. The autorouting took about 40 seconds, and the job time has been about 135 hours overall (but that might include time on a previous job I may have used as a "starter". Here is the latest schematic image:










Here is the silkscreen of the board(s):










I just got an on-line quote from https://ecommerce.pcbfabexpress.com/ for 5 boards with 5 day turn for $51 each. There might be an extra charge for a three-part board, although it can be used as-is for the prototype. I can easily enough saw along the keepouts, or rig up a dremel in the milling machine to score the boards. I will also try a couple other sources, but my experience over the last 10-15 years with this company is that they are the best for prototype quantities. For 50 boards in 10 days the cost is about $12.50 each, and that will be hard to beat, although I have usually ordered from www.pcbcart.com in China for quantities, and once the tooling cost is paid (about $200), a board like this might cost $5 each in 50 piece lots.


----------



## PStechPaul (May 1, 2012)

I did an LTSpice simulation on a modified version of the proposed J1772 sensor circuit using a low power PIC. I simulated the PIC with a 100k resistor, which draws 50 uA at 5 V. That should be about right. I had to play with the resistor values to compensate for the draw of the PIC voltage supply, so that the voltages seen by the EVSE will be as expected, 9V and 6V peak. Here is the simulation at 10%, 25%, 50% and 75% duty cycle:





































10% (6A) 3.32 and 2.57V
25% (15A) 3.75 and 3.40V
50% (30A) 4.07 and 3.72V
75% (45A) 4.31 and 3.91V

So the PIC can read its own power supply voltage by using its internal 1.024 or 2.048V reference. It does so by using Vcc as reference, and reading its reference as an input. Thus the ADC reading will be lower as the power supply voltage increases. I will need to add the additional components shown in the simulation schematic.

Here is a close-up view of the waveforms as the additional shunt resistor is applied, for a 75% duty cycle:


----------



## PStechPaul (May 1, 2012)

I made some final changes to the board and I will be ordering as soon as I receive firm quotes. PCBfabExpress said that each additional design would be $20, so final cost is about $300 for 5 pieces in 5 days, plus shipping. I asked about getting 2 oz copper, and they said that was standard. But then they said that meant 1 oz on top and 1 oz on bottom. Huh?!

I submitted a file to www.eiconnect.com and hope to get a quote tomorrow. And I also asked for an on-line quote from www.pcbcart.com, and it seems to be only $102 total for 5 PCBs in 5 days. I had expected a tooling charge of about $200 and shipping about $20. I sent a question about that. It seems too good to be true 

I updated the schematic and silkscreen images which you can see above.


----------



## PStechPaul (May 1, 2012)

I have just now sent off the files for the three-part PCB. I should have them in about 10 days. I made a deal with www.eiconnect.com that Trump would say is "awesome", where I negotiated the original $400 quote down to $175. It would still be cheaper from China but for $75 I'd rather "buy American" and also maintain a good business relationship with that company, with whom I have done business for probably 20 years.

I found a few errors as I checked the layout, and it's good that I did, because some of them were pretty severe. Mostly it involved the position of the displays and mounting holes and pushbutton switches. I had laid out the board hoping to be able to use the original Adafruit uLCD144G2 as well as the 1.8" 160x128 TFT that is cheap and easy to get, as well as a smaller 128x128 version that has the same interface and command set. But that one will require some sort of adapter or flexible connector to fit the existing cutout, and the 1.8" display will need the cutout enlarged to see all of it. I could not get the uLCD144G2 connector properly positioned, but it is only about 0.1" off and the pins might bend that much.

This should be fun, and it has already been a challenge to design the boards. I expect to have some errors and problems with the control/driver PCB, but I learned that eiconnect has an offer of $75 for 3 boards in 7 days as long as they are no larger than 5" x 5", and these boards are 4.75" x 3.75". 

Well, now I need to rest my eyeballs after staring at the computer screen for many hours.


----------



## PStechPaul (May 1, 2012)

I just ran a BOM on this set of boards and the total comes to about $277. That includes about $100 for the IGBTs, $10 for the inductors, $20 for power board capacitors, and $9 for the Hall effect current sensor. So the control/driver board and precharge/discharge boards are about $150 in parts plus the PCBs themselves. Hopefully this will be able to make some of the existing EMW chargers more reliable and easier to work on.


----------



## PStechPaul (May 1, 2012)

I got the parts order from Mouser on Wednesday, and on Friday May 20 I received 7 boards from Electronic Interconnect. I was pleasantly surprised by such fast delivery, and I got two extra boards at no charge, so the $175 works out to only $25 per board. And they are actually three boards on one piece, scored for separating them as needed:










I started by soldering a tiny MOSFET in an SOT323 package, to an even smaller footprint for an SOT523 package. You can see the footprint for Q4:










Next I soldered U7, which is the PIC10F320 in an SOT-23-6 package:










I'm holding it in spring-loaded tweezers while I solder it with 0.3mm 63/37 solder. Today I finished the assembly except for one more inductor:










The back of the board:










Another view:










As I assembled and tested the board, I found some errors and issues, but all could be corrected:

1. 5/20/16 Decal for Q4 should be SOT323 (not SOT523). Can still mount component.

2. 5/21/15 Decal for PCB1 (Arduino Pro Mini) is upside down. A4, A5, A6, A7 and FTDI connections reversed. Affects pilot and proximity sense.

3. 5/21/16 Schematic decal for ST7735 display pin names reversed

4. 5/21/16 Display SCK and SDA went to A5 and A4. Should go to D13 (MISO) and D11 (MOSI). Cut tracks and jumper.

5. 5/21/16 D5, D18 hole size too small

6. 5/21/16 PS2, PS3 won't fit high isolation versions. Drill holes, cut tracks, jumper. Outline should be larger.

7. 5/21/16 Bluetooth header should be 6 pins.

8. C2, C19, C26 pads too close together.

9. Should add GRN LEDs across RED D4 and D16.


----------



## PStechPaul (May 1, 2012)

I am now at the point where I need to test various portions of the design, so I am making a sketch that will take care of that. So far I just used the "graphicstest" sketch for the TFT display as a starting point, and it just writes "EMW PFC PCB Test" to the screen in large letters, and then counts seconds while an alternating on/off signal is sent to the IGBT gate of Q2, which is where the PWM for the output occurs. There are no IGBTs yet, but the anti-parallel red and green LEDs flash indicating proper gate drive.

There was a problem on power-up where one of the DC-DC converters (for the PFC section) got really hot and it was drawing over 600 mA on the 12V input. It was an old V-Infinity VESD2-S12-D15-SIP device that I had pulled from a junk driver PCB, and it had some leads partially pulled loose, so I had fixed it with epoxy and it tested OK. But I figured it had just finally failed so I pulled it out, causing one of the leads to pull out completely. Then I noticed that one of the 15V zeners on the output was reversed, and presented essentially a short.

When I programmed the Arduino with the simple test program through the FTDI connection, the board was still connected to the power supply, and although the output was dialed to zero, it read 3.6V. Also, the LEDs on the gate were flashing red/green. Thus the DC-DC converter must have been working even on the nominal 5VDC from the FTDI and USB, and the SI8261 gate drivers also worked on that voltage. However, when I increased the voltage to 12V, the red gate LED stayed lit. I discovered that I had connected the 499R pullup resistor R27 to +12V and it was conducting through the D9 output of the PIC which on the 5V supply.

Well, I'll work on the test program tomorrow. I'll be testing the various I/O of the Arduino:

A0: Charge current sense by means of a 0.01R sense resistor and HV7800 sensor
A1: Battery voltage sense using differential amplifier
A2: Heat sink temperature
A3: Mains voltage sense, reading AC or DC using differential amplifier and level shifter
A4: Not used (SDA)
A5: Not used (SCL)
A6: Not connected (J1772 Pilot Sense)
A7: Not connected (Proximity Sense)

D2: <= Pushbutton S1
D3: <= Pushbutton S2
D4: => EOC Out
D5: <= BMS In
D6: => J1772 (or spare)
D7: => TFT CS
D8: => TFT A0
D9: => PWM
D10: => SD CS (or spare)
D11: => MISO
D12: <= MOSI
D13: => SCLK

 There is also an overcurrent sense circuit that uses the voltage on the current sense resistor for the PFC section to monitor the input current. It just lights a red LED and provides an opto-isolated low signal that could connect to a digital input pin of the Arduino. It could also possibly trip a line relay to shut down the charger.

That's really all there is under control of the Arduino. I will need to test the precharge and discharge circuitry, which are separate, as well as the PFC circuit, and the main power section. I may need to make a J1772 simularor to test that circuitry, which will operate by means of the PIC. And I should connect A7 for proximity sense, but I'm not sure how to use it. Essentially it should provide an "ignition" interlock to prevent the car from driving while connected to the EVSE.


----------



## Rastusmalinus (Sep 26, 2011)

It's great that you're doing this. The original needed much more collaboration and testing before being sold. It created unneeded confusion with so many versions being released, and it made the thread hard to navigate.


----------



## PStechPaul (May 1, 2012)

Thanks. I welcome any and all comments, suggestions, criticisms, and attaboys! I just verified that the input and output voltages are being measured, but I need to make an RMS or absolute value averaging algorithm so the AC mains voltage can be displayed. I am using the HV7800 device to measure the battery charge current through a sense resistor on the high side, but it needs a voltage differential to work. I also need to add a 20x gain amplifier, as it is 1:1 and the maximum for the 0.01 ohm sense resistor is 200 mV at 20A, which should be boosted to 4V for the ADC.

I made a little video clip showing the precharge circuit working. When I apply about 100 VAC to the input, it takes about 3 seconds for the 22 ohm resistor to charge the 560 uF capacitor sufficiently to close the precharge relay. The TC is actually only 12.3 mSec but this circuit waits until the capacitor is almost fully charged, which may take 5 TCs or longer. It measures the voltage on the 22 ohm resistor and operates when that voltage drops below a few volts, and there is some extra capacitance there. It works faster when I use closer to 120 VAC. There is also probably some delay in the 12V supply. I can easily change some components and speed it up. The real test will be on the EMW charger itself, with about 9000 uF.

http://enginuitysystems.com/files/EMW/EMW_PFC_PCB_Test1_2961.AVI


----------



## PStechPaul (May 1, 2012)

Something I noticed about the Arduino programming is that setting analog pins as inputs may make the corresponding digital pins to be inputs. Here is code from the original EMW V13 charger firmware:


```
//---------------- pin-out constants ----------------
//========== analog pins
const byte pin_C=0; // output current pin
const byte pin_bV=1; // output / battery voltage pin
const byte pin_heatSinkT=2; // charger heatsink temp - for thermal derating 
const byte pin_12Vsense=3; // implementing undervoltage protection
const byte pin_temp2=4; // 4 - spare prewired as temp input
const byte pin_mV=5;
const byte pin_mC=7; // will only work in V12 control boards (June 2013). needed only for full digital PFC control

//========== digital pins
// 0/1 reserved for serial comms with display etc
const byte pin_pwrCtrlButton=2; // this is wired to the button (used for menu step)
const byte pin_pwrCtrl2Button=3; // this is wired to the button2 (used for menu select)
const byte pin_inrelay=4; // precharges input caps - normally pin 4, in some units pin 6 running fan relay
const byte pin_outrelay=5; // protects from reverse polarity on traction battery, precharges output resistors
const byte pin_PWMpulldown=6; 
const byte pin_J1772=7; // J1772 pilot input. 1k is hardwired on V14+ pcbs so J1772 will power on on connect
const byte pin_fan=8; // fan control - this is pin4 in all kits shipped before Mar '2912
const byte pin_PWM=9; // main PWM pin

  // set analog input pins
  pinMode(pin_C, INPUT);
  pinMode(pin_bV, INPUT);
  pinMode(pin_heatSinkT, INPUT);
  pinMode(pin_12Vsense, INPUT);
  pinMode(pin_mV, INPUT);

  // digital inputs
  pinMode(pin_pwrCtrlButton, INPUT);
  pinMode(pin_pwrCtrl2Button, INPUT);
  pinMode(pin_J1772, INPUT);
  pinMode(pin_BMS, INPUT);
```
In my design I noticed it because the display stopped working after I tried to set the analog pins to inputs, 


```
#define TFT_CS     7
#define TFT_RST    0
#define TFT_DC     8

//========== analog pins
#define PIN_Iout      0         // output current pin
#define PIN_Vbatt     1         // output / battery voltage pin
#define PIN_HST       2         // charger heatsink temp - for thermal derating 
#define PIN_Vmains    3         // mains voltage
#define PIN_A4        4         // spare 4
#define PIN_A5        5         // spare 5
#define PIN_Pilot     6         // J1772 Pilot sense
#define PIN_Prox      7         // J1772 Proximity sense
// 0/1 reserved for serial comms with display etc
#define PIN_btSelect  2         // this is wired to the button (used for menu step)
#define PIN_btStep    3         // this is wired to the button (used for menu step)
#define PIN_EOCout    4         // 
#define PIN_BMSin     5         // 
#define PIN_J1772     6         // spare
#define PIN_TFT_CS    7         // Display CS
#define PIN_TFT_A0    8         // Display A0 C/D
#define PIN_PWM       9         // main PWM pin

  // set analog input pins
//  pinMode(PIN_Iout, INPUT);
//  pinMode(PIN_Vbatt, INPUT);
//  pinMode(PIN_HST, INPUT);
//  pinMode(PIN_Vmains, INPUT);
//  pinMode(PIN_Pilot, INPUT);
//  pinMode(PIN_Prox, INPUT);
```
The ADC functions work OK with the overlapped pin numbers. There is an Arduino header file (pins_arduino.h)that defines the analog pins as 14..21:


```
static const uint8_t A0 = 14;
static const uint8_t A1 = 15;
static const uint8_t A2 = 16;
static const uint8_t A3 = 17;
static const uint8_t A4 = 18;
static const uint8_t A5 = 19;
static const uint8_t A6 = 20;
static const uint8_t A7 = 21;
```
But under the Mega folder, the same header file defines them differently:

```
static const uint8_t A0 = 54;
static const uint8_t A1 = 55;
static const uint8_t A2 = 56;
static const uint8_t A3 = 57;
static const uint8_t A4 = 58;
static const uint8_t A5 = 59;
static const uint8_t A6 = 60;
static const uint8_t A7 = 61;
static const uint8_t A8 = 62;
static const uint8_t A9 = 63;
static const uint8_t A10 = 64;
static const uint8_t A11 = 65;
static const uint8_t A12 = 66;
static const uint8_t A13 = 67;
static const uint8_t A14 = 68;
static const uint8_t A15 = 69;
```


----------



## bicycleguy (Aug 13, 2015)

The boards look awesome. Especially nice to see focused well lit photos on this site! I must say until I saw these boards I wondered if you were one of the criticizers, not the actual doers. Well done!

A few observations.
The 'Pro Mini' is not the mega which refers to the MEGA 2560 54 digital I/O parts even though mega is in the ATmega328 name. So somehow you are using the wrong header files. The Arduino IDE takes care of setting this up when you select the correct board ('Arduino Pro or Pro Mini') in the tools menu. A lot goes on behind the scenes in the Arduino IDE to help junior programmers and enevitably frustrate senior programmers. 

OpenEVSE uses the same ATmega328. FYI they have a modified version of the board file that you select in the tools menu. Their mod makes some changes to the fuse bit programming to remove the boot loader for more memory.

The whole J1772 thing is correctly implemented and carefully explained on the OpenEVSE site. I would recommend following it closely. Auto manufactures have used subtle details of its workings such as on my Chevy Spark if you plugin then quickly unplug then re-plugin the connector the car will switch to immediate charging mode from programmed mode.

The DCFC connector you pictured actually uses CANbus signaling within the DC high voltage pins and would be neat to implement on the output of your charger. I plan on looking at this as a way to get around the Spark's 3.3kw internal charger. The HV DC pins can charge the battery at 40kw. A dual CAN bus can be implemented on an Arduino mega 2560 or a single on a Teensy. I have built both but have not figured out the interface and required protocol for the DCFC yet.


----------



## PStechPaul (May 1, 2012)

Thanks for the information and encouragement. The correct header files are probably being used, since I do have the 'Arduino Pro or Pro Mini' selected. But it didn't like setting the analog pins to inputs, as that seems to set them as digital input. It might not matter if the Arduino commands are used: analogRead(), digitalWrite(), digitalRead(), but when using those pin assignments outside those functions, it does not seem to work properly. I also tried using A0..A7 and it didn't like that either.

The Arduino environment seems to be a convoluted mess, with strangely nested folder names:


```
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino
C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr
```
At this point I consider the J1772 and BMS interfaces to be beyond the immediate scope of this project, but I think my design should conform to the standard. I think the OpenEVSE project offers a J1772 simulator and other helpful items. I can't understand why Valery chose to use a 1k resistor in his charger, and I don't see how it can even work at all.

I found details for the OpenEVSE controller:

http://support.openevse.com/support/solutions/articles/6000052070-theory-of-operation


















































I have registered for the OpenEVSE forum and I have posted a new topic to discuss the J1772 EVSE protocol and making or purchasing a simulator. 
http://support.openevse.com/support/discussions


----------



## bicycleguy (Aug 13, 2015)

PStechPaul said:


> Something I noticed about the Arduino programming is that setting analog pins as inputs may make the corresponding digital pins to be inputs. Here is code from the original EMW V13 charger firmware:
> 
> 
> ```
> ...


----------



## PStechPaul (May 1, 2012)

My schematic seems to be just about the same:










Note that PD6 and PD7 are also labeled as AIN0 and AIN1, but looking at the specs for the ATmega, those are inputs to the comparator. Here is more info:
http://emulare.sourceforge.net/AnalogComparator.php

I bought some newer Pro Minis that have the A4, A5, A6, and A7 on a single SIP header opposite the FTDI header. Others have one or two two-pin headers located inside the main headers.

http://www.banggood.com/5Pcs-Pro-Mi...ved-Version-Module-For-Arduino-p-1009204.html








Ten dollars for five pieces.


----------



## bicycleguy (Aug 13, 2015)

I've got to quit editing my posts. So I'll add here in case you missed it. Again the schematic doesn't show it but the digital pins are just 0,1,2 ect. The analog input are A0,A1 ect. You don't use pinmode() with the A1-A7 pins, unless you want to make them digital inputs or outputs. 

Arduino's idea of analog out is pwm. Hope your not planning on using any. Some boards, like the Teensy have actual analog out.


----------



## PStechPaul (May 1, 2012)

I will be using the native PWM registers and functions for that purpose, and only on one output. And I will be using similar means for interrupt-driven analog input. The following is partially taken from the EMW firmware sketch:


```
ISR(ADC_vect) {                     // Analog->Digital Conversion Complete
  byte ul, uh;
  cli(); // disable interrupt until function exit. otherwise nested interrupts...
  ul=ADCL;
  uh=ADCH;
  sei();
  
  ADCval = (uh << 8 | ul);         // assuming ADLAR=0
  switch(ADMUX & B00000111) {
   case PIN_Iout: {                   // this is measured at 2.4 kHz frequency
     if(outC_ADC==0) {
       outC_ADC_f=outC_ADC=ADCval;
     } else {
       // 16 cycles is 8ms here or a full haversine period
       outC_ADC_f=(outC_ADC_f*15+ADCval)/16; // this emulates an RC filter with time constant of ~half of averaged periods
       outC_ADC=int(outC_ADC_f);
     }
     break;
   }
   // rest of vars measured at 300 Hz
   case PIN_Vbatt: {
     if(outV_ADC==0) {
       outV_ADC=ADCval;
     } else {
       outV_ADC=(outV_ADC+ADCval)/2;
     }
     break;
   }
   case PIN_Vmains: {
     if(mainsV_ADC==0)
       mainsV_ADC=ADCval; 
     else
       mainsV_ADC=(mainsV_ADC+ADCval)/2;
     break;
   }
   case PIN_HST: {
     if(T_ADC==0)
       T_ADC=ADCval;
     else {
       T_ADC=(T_ADC+ADCval)/2;
     }
     break;
   }
   default: break;
  }
}

void sampleInterrupt() {  // 9600 Hz
  int  ADCsel;
  tickerPWM++;                                // increments at 9600 Hz = 104 uSec.
  if( tickerPWM >= 9600 ) {
    tickerPWM_sec++;                         // increments at 1 second rate
    tickerPWM = 0;   
    if(Mode==RUN)
      Tchg_sec++;
    }
  
  if( (tickerPWM&B00000001)==0 ) {            // (Change to b00000011 for 19.6 kHz)
    ADCsel = ( (tickerPWM>>1) & B00001111);   // ADCsel = 0..15 (Change to 2 for 19.6 kHz)
    ADMUX &= B11111000;                       // reset the channel to zero
    // then current is measured every second cycle - or at ~2kHz frequency
    if( (ADCsel&B00000001)==0 )
      ADMUX |= PIN_Iout;
    else if( (ADCsel>>1) > 0 )
      ADMUX |= (ADCsel>>1);                   // Values from 1 to 7
    ADCSRA |= B11000000;                      // manually trigger next one
    } // ADMUX will be the value of the pin being measured: 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7
  }
```


----------



## bicycleguy (Aug 13, 2015)

The digital input pins can be set with an internal 20K pull-up resistor to save a few resistors:

pinMode(pin_pwrCtrlButton,INPUT_PULLUP);

Don't do this on the MOSI.

I have bad eyes and can't quit read the schematic. Can you post a higher resolution or vector pdf version?

thanks


----------



## bicycleguy (Aug 13, 2015)

PStechPaul said:


> I will be using the native PWM registers and functions for that purpose, and only on one output. And I will be using similar means for interrupt-driven analog input. The following is partially taken from the EMW firmware sketch:
> 
> ...
> 
> Sorry for the confusion, they work fine for PWM, just not analogue output without filtering.


----------



## PStechPaul (May 1, 2012)

Here is an updated schematic in PNG format. This is the design to which the PCBs were made, but some changes will be made according to the issues I am finding.










Here is a PDF:
http://enginuitysystems.com/files/EMW/EMW_PFC_PCB_retrofit_1.pdf

Thanks for the comments. I am more familiar with the Microchip PIC platform and originally I was going to use one of their devices for this project, but since then I have become more comfortable with the AVR Atmel products and the Arduino suite. I do miss the MPLAB and MPLABX software and hardware simulators and debuggers, but there is an equivalent of sorts that I have tried to use with limited success:

http://www.visualmicro.com/post/2012/05/05/Debug-Arduino-Overview.aspx

http://www.atmel.com/tools/atmelstudio.aspx


----------



## WolfTronix (Feb 8, 2016)

I did the opposite switched from Atmel to PIC...
Got tired of Atmel discontinuing micros, and then releasing "almost" backwards compatible micros in there place...

My favorite go to micro is the:
http://www.microchip.com/wwwproducts/en/dsPIC33EP256MU810

Makes layout easier, just get over to any random I/O pin, and then use peripheral pin select to map the functions to that pin in firmware.


----------



## PStechPaul (May 1, 2012)

I chose to retain the Arduino core and much of the hardware so that it would be compatible with the existing chargers, and so their owners could upgrade and maintain their firmware more easily.

Here is what I used to display the input mains AC voltage using a true RMS calculation:


```
ISR(ADC_vect) {                     // Analog->Digital Conversion Complete
...
   // rest of vars measured at 300 Hz
   case PIN_Vmains: {
     mainsV_ADC = ADCval;
     mainsV_ADC -= ADC_OFFSET;
     mainsV_Count++;
     mainsV_Sum_Squares += mainsV_ADC*mainsV_ADC;   // Sum of squares
     if( mainsV_Count >= MAX_mainsV_Count ) {
       mainsV_RMS = sqrt( (float)mainsV_Sum_Squares / MAX_mainsV_Count );
       mainsV_Count = 0;
       mainsV_Sum_Squares = 0; }
     break;
   }
```
Something else I just discovered. If you want to use the TimerOne library for PWM, you can't use the pin for a digitalWrite(). The documentation says that it will break analogWrite(), which is the generic PWM function. Actually you can do it but you must use Timer1.initialize() after the digitalWrite and before PWM.


----------



## PStechPaul (May 1, 2012)

Yesterday I was able to install the IGBTs and get some output from a 90-100 VAC supply. But I found some issues.

I am using the HV7800 in my charger design, along with a 0.003 ohm sense resistor (actually 0.01 ohm in this low-power prototype). The HV7800 has a gain of 1, so for 30 amps that is 90 mV and the ADC has a 5V reference. I added a Microchip MCP6G01 selectable gain amplifier for 50x and thus 2.5 volts at 30 amps.

But there is a problem when the charger is first energized and the output has no voltage, and the current sense signal is erratic and ambiguous until there is at least 8 volts on the output. I will probably have to use the Hall device instead. It is about $9 but when the cost of the shunt resistor, HV7800, and MCP6G01 are compared, it's not much cheaper. 

[edit1]
There is also a more serious problem. Since the DC bus (-) and (+) rails are made using a FWB off the 120 VAC line, there is substantial AC voltage on the rails, which actually goes negative WRT to neutral and earth ground, and the HV7800 draws considerable current when reverse biased. I found this by attempting to connect my scope ground clip to the AGND of the control circuitry, and promptly tripped the GFCI fortunately supplying my bench. The Hall device is now installed and working well.










[edit2]
I found some interesting things about the Arduino tft.print() function. If the variable being printed is a "float", it seems to format it with two decimal places. I'm not sure if this is common to all varieties of the Arduino print() functions, but apparently the sprintf() function does not work for a %f floating point argument. Searching a bit, I found a good way to implement this using a built-in AVR function, posted here:
http://dereenigne.org/arduino/arduino-float-to-string


```
// dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf);

void loop() {
  char  charBuf[16];
  
  while( (digitalRead(PIN_btSelect) == HIGH) ) {
    tft.setCursor(1, 1);
    tft.println("Out Curr: ");
    dtostrf(outC_Avg, 7, 2, charBuf);
    tft.print(charBuf);
    tft.println("   ");
    tft.println("Out VDC: ");
    dtostrf(outV_Avg, 6, 1, charBuf);
    tft.print(charBuf);
    tft.println("   ");
    tft.println("Mains V: ");
    tft.print(mainsV_RMS);
    tft.println("   ");
    tft.println("Duty: ");
    tft.print(duty);
    tft.println("   ");
    delay(200);
    }
```
Note that the MainsV_RMS is a floating point variable, and it prints on the display with two decimal digits.


----------



## PStechPaul (May 1, 2012)

The Hall Sensor CSLT6B100 seems like a pretty good bargain from Mouser. It is about twice as much from DigiKey and Newark. But there is another option I might consider for a lower power charger: the ACS712. It is available in 5, 20, and 30 amp varieties, and cost is about $5-$3 from Digikey:
http://www.digikey.com/product-deta...s-llc/ACS712ELCTR-30A-T/620-1191-1-ND/1284608

I have also found complete modules for much less, about $2 each:
http://www.banggood.com/3Pcs-30A-New-Range-Current-Sensor-Module-Board-For-ACS712-p-980106.html

Also, I wonder how hard it would be to use a small ferrite toroid with a slot cut in it, and put a Hall-effect magnetic field sensor there to sense current. Such linear devices are in TO-92 packages and cost less than $1:
http://www.mouser.com/ProductDetail...5vlrqIFXt5VIp9k9SfL8HB5FzD77rimrkfAyTL0QE6w==
http://www.mouser.com/ds/2/405/drv5053-373175.pdf

I don't mind paying $9 for the device I have, but it is single sourced and could be discontinued. I think I'll buy a couple of these and see how they work.

BTW, I was able to get significant current out of the power board by controlling the PWM duty cycle, as shown in th picture above. The duty cycle is actually (1023-duty)/1023:


```
Duty....Iout...Vout...Rload
1.56%   1.03   0.0    0.10
2.05%   1.52   0.0    0.10
2.54%   2.47   0.0    0.10
2.93%   3.52   0.5    0.10
3.32%   4.55   0.5    0.10
4.01%   5.59   1.1    0.10
0.10%   ----   8.7    OPEN
0.40%   ----   54.5   OPEN
0.88%   ----   93.6   OPEN
```


----------



## bicycleguy (Aug 13, 2015)

PStechPaul said:


> Yesterday I was able to install the IGBTs and get some output from a 90-100 VAC supply. But I found some issues.
> 
> ...
> 
> ...


----------



## PStechPaul (May 1, 2012)

I didn't know that, and it didn't come up on my initial search. But I found the Arduino library item that defines the behavior of the various "print" and "println" functions, in:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\Print.h


```
size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);

    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);
    size_t println(const Printable&);
    size_t println(void);
```
I also ran a simulation at 4% PWM and it seems to show reasonably accurate results compared to actual circuit. I used a MOSFET rather than IGBT so that may account for the differences:


----------



## PStechPaul (May 1, 2012)

Additional simulation points:


```
[FONT=Courier New]
Duty....Load.....Current...Pout....Pin.....Eff
 4%     0R3      6.69      13.4     31.5   42%  (MUR460)
12%     0R3      24.9     185.5    307.6   60%
12%     0R3      26.6     215.2    327.8   66%  (RF2001NS3D)
12%     0R3      28.1     236.1    341.7   69%  (RFN60TS6D)
12%     0R3      29.9     268.9    360.7   74%  (MBR20100CT)
[/FONT][FONT=Courier New][FONT=Courier New] 4%     0R3       9.3      26.0     43.4   60%  (MBR20100CT)
[/FONT] 4%     0R3+24V  0.59      14.2     15.6   91%
 8%     0R3+24V  2.14      52.8     58.7   90%
12%     0R3+24V  4.30     111.5    121.6   92%
16%     0R3+24V  6.77     175.2    199.6   88%
20%     0R3+24V  9.33     247.2    284.2   87%[/FONT]
```


[edit]The most power in the first two simulations seems to be in D1 (7.8W) and D2 (7.8W). They are standard 4A silicon devices MUR460. I replaced them with a fast recovery 10A diode RF2001NS3D and they still dissipate 7.6W and 7.8W. A 30A diode RFN60TS6D was a bit better, 7.3W and 7.6W. Using a Schottky 100V 10A device (MBR20100CT) was best of all with 6.4 watts in D1 and 6.6 watts in D2. The charger actually uses the IGBT body diode so results will differ. The Schottky has lower Vf and very little reverse recovery.

It looks like the best choices are found among SiC devices:

http://www.mouser.com/ProductDetail...=sGAEpiMZZMtQ8nqTKtFS/NX0wTwLhSQZ0Vn1Um9gNm4= (20A, 200V, $1.00)

http://www.mouser.com/ProductDetail...=sGAEpiMZZMtQ8nqTKtFS/F5QSfLqxcKqh5IpL2Y3KxQ= (30A, 300V, $1.38)

http://www.mouser.com/ProductDetail...=sGAEpiMZZMtQ8nqTKtFS/CKUxMvjsmGzB362KjcWM1Y= (40A, 250V, $1.55)

http://www.mouser.com/ProductDetail...=sGAEpiMZZMtQ8nqTKtFS/O/9ct72N6MlSbImGpRMgAM= (20A, 600V, $1.70)

http://www.mouser.com/ProductDetail...=sGAEpiMZZMtQ8nqTKtFS/AV4D1MUJvptyhGTyCSwS1c= (40A, 300V, $3.26)


----------



## bicycleguy (Aug 13, 2015)

Thought I'd try LTSpice for the first time. As a former mechanical engineer in electronics packaging I'm always fiddling with electronics. I have many questions.

Was able to reproduce the above results as shown in the lower left trace. 








For the upper left trace I increased the duty cycle to 12% and got resistor currents around 26A but a lot of sawtooth when I look at greater time scale. Is this correct? Does this say C4 should be a lot bigger?

What do C2 and C3 do? When I take them out of the analysis nothing changes.

Here I tried to reproduce the "12% 0R3+24V 4.30 108.8 130 84%" you posted.
Is this how to do the 0R3+24V? I get a lot of ringing on V(in) but the I(R1) looks about right.










I'm a little lost on how this schematic relates to the EMW schematic. Also any insight into how realistic this simulation is or what to look out for would be appreciated.


----------



## PStechPaul (May 1, 2012)

The schematic shows the buck switching section of the power board. The EMW design also has an optional PFC section, which should provide a smoother source of DC that will eliminate much of the 120 Hz ripple. I also saw similar ringing across M1, and it didn't change much when I added 500 pF parallel capacitance on the inductor, or up to 47 ohms in series with the gate of the MOSFET. Snubbers could be added to reduce the ringing at the expense of wasted power.

I found that some of my efficiency readings were wrong. I had used an approximate time for the integration, but it changes a lot (for the better) when an exact multiple of half-cycles is used. Otherwise the energy transfer in and out of the capacitors is counted as extra power. I'll change the values in my previous post.

The additional 4.7 uF capacitors are low ESR types that can handle surge currents and high frequency better than the large electrolytics. The schematic does not show all of the additional parasitic parameters of components. 

I can supply the ASC file for my simulation, but it looks like you have it pretty well modeled.


----------



## PStechPaul (May 1, 2012)

Just an announcement that I had my lumbar spine surgery June 16 and it went well, and now I am int the process of recuperation. Probably will be home in about 2 weeks, then still on limited duty for a month.


----------



## Rastusmalinus (Sep 26, 2011)

PStechPaul said:


> Just an announcement that I had my lumbar spine surgery June 16 and it went well, and now I am int the process of recuperation. Probably will be home in about 2 weeks, then still on limited duty for a month.


I hope your recovery is as painless as possible.


----------



## PStechPaul (May 1, 2012)

Surprisingly, there was never any major pain, and I'm feeling fine, although I still need to wear the back brace until I see the surgeon in two weeks. Then I will need to do some PT to strengthen the muscles and regain flexibility. I have talked to other people who said how terrible their recovery was, and there are some who actually got worse after surgery. There is actually a medical name for this: Failed Back [Surgery] Syndrome, and codes ICD9 and ICD10:

http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3205485/

https://en.wikipedia.org/wiki/Failed_back_syndrome

Several things probably influenced my favorable outcome:

1. I had a great surgeon and surgical team

2. I've built up a tolerance to back pain over 30 years 

3. I had a very positive attitude toward the surgery

4. I remained as active as possible pre-surgery to maintain good cardiovascular and muscular-skeletal health.


----------



## arber333 (Dec 13, 2010)

Paul i noticed something interesting while trying to setup my 3phase charger for single phase use. Maybe it can be appiled to simplify charger.

I have a 370Vdc pack and i wanted to setup charger to have the least trouble when connecting to Level 1 station (or home socket). 
While using 3ph i can get a pretty good PF due to symetric load. However when i charged friends 200Vdc pack from single phase i got cca 35% waste power!!! 
Now i cant automaticaly charge from single phase due to 370Vdc being way too high for simple buck charger, so i used a trick from computer PSUs. I put N line trough fuse breaker towards input caps middle connection and i got 600Vdc out of it (voltage doubler).
I was cautious at first charging only at 2kW. But last week i forgot to set the limit and charger pulled 5kW for some 5 minutes out of 230Vac 20A single phase, without throwing the fuse breaker!!! 
I dont have anyone to ask for confirmation, but it seems that voltage doubler is actually increasing PF some. Any comments?


----------



## PStechPaul (May 1, 2012)

I tried a simulation of a 120 VAC to 320 VDC FWB doubler. It has an input of 1.07 kW and output of 1.03 kW, with input of 117.8V at 14.9A and 1.75 kW. That's a PF of 1.03/1.75 = 0.59. 










If you got an output of 5kW from a 230 VAC that would be 21.7A at unity PF and 37 amps at 0.59 PF. It is possible for a 20A breaker to stay closed at 2x rating for quite a while, and 5 minutes seems within normal tolerance.

An Eaton 3 phase 70-100 amp breaker can take 160 seconds to trip at 2x, and easily as long as 5 minutes at 1.5x:









http://www.eaton.com/Eaton/Products...ort/Documentation/TimeCurrentCurves/index.htm


----------



## arber333 (Dec 13, 2010)

hmm... it seems so, today when i arrive home i will leave car on single phase at 5kw. Well see how long it will sustain power. 

A


----------



## fmilner (May 11, 2021)

Hey All: I know this thread is more dead than alive, but I need some help. I got a 10kw V2 EMW charger, and I want to upload new code. Problem is since it is so old I don't want to upload V14 firmware as I don't know which of the #define switches to comment out and which to leave.

Does anyone have any code that would work for a v2 charger? Here are some pictures of my charger if that helps identify hardware. Happy to take more if needed or read ICs.


----------



## arber333 (Dec 13, 2010)

fmilner said:


> Hey All: I know this thread is more dead than alive, but I need some help. I got a 10kw V2 EMW charger, and I want to upload new code. Problem is since it is so old I don't want to upload V14 firmware as I don't know which of the #define switches to comment out and which to leave.
> 
> Does anyone have any code that would work for a v2 charger? Here are some pictures of my charger if that helps identify hardware. Happy to take more if needed or read ICs.


I think all EMW chargers will work with new code, you just have to go through switches and confirm on the board that you either have the sensors or not. Also i think new code is working with PID and for that you need to change the gain on LM211 chip on the main board. Valery wrote something about it in the latest instructions for legacy devices. I think you need to replace some caps...

I can say that PID really makes this a good charger. Before there were quite big current swings, but now current is held within +/-0.5A range. I think it is worth the effort.


----------



## fmilner (May 11, 2021)

arber333 said:


> I think all EMW chargers will work with new code, you just have to go through switches and confirm on the board that you either have the sensors or not. Also i think new code is working with PID and for that you need to change the gain on LM211 chip on the main board. Valery wrote something about it in the latest instructions for legacy devices. I think you need to replace some caps...
> 
> I can say that PID really makes this a good charger. Before there were quite big current swings, but now current is held within +/-0.5A range. I think it is worth the effort.


Thank you so much for your reply!
I am running on that older version and certainly see those current swings. I haven't yet found Valery's post about the legacy devices... do you have a link or a thread? I'll edit this post if I do. Again - thank you.


----------



## arber333 (Dec 13, 2010)

fmilner said:


> Thank you so much for your reply!
> I am running on that older version and certainly see those current swings. I haven't yet found Valery's post about the legacy devices... do you have a link or a thread? I'll edit this post if I do. Again - thank you.


See here some files, curtesy of *PStechPaul*


Index of /files/EMW/V14


Ok then. Right now i am trying to build me a liquid cooled version of EMW charger for my car. I want this to be 3phase capable and able to work with 20kW power levels to get the most out of the EU EVSE. Since i see there is interest i will document my build and point out the obvious building blocks. Stay tuned...


----------



## fmilner (May 11, 2021)

arber333 said:


> See here some files, curtesy of *PStechPaul*
> 
> 
> Index of /files/EMW/V14
> ...


Thank you! I now have new(ish, i did different version because I wanted less switches to get wrong) working. From there I can go to a version with PID. However, it felt pretty good when it booted up and gave me a custom message on the LCD display! I didn't have to mess with any capacitors to get the firmware I found up and running, which was good. Best of luck on your liquid cooled charger.


----------

