открытый проект зарядка для электромобиля нисан лиф и др тип 1 j1772

daniilguz
Offline
Зарегистрирован: 13.01.2020

В данной теме пытаюсь собрать зарядку для электромобиля со свободным ПО и регулируемым током из доступных деталей. Буду благодарен за помощь в разработке.

на основе материалов изложеных по следующей ссылке:

https://www.instructables.com/id/Arduino-EV-J1772-Charging-Station/

Защита Узо отдельным блоком, реле отдельным модулем, светодиод  отдельным модулем,включать заряд  реле 30 А или пускатель можете выбрать самостоятельно.

Еще нужно добавить несколько терморезисторов для измерения температуры пистолета самого блока и вилки где он подключается в розетку (пока в разработке.)

Перечень материалов:

Коробка пластиковая 200*100*70

Ардуино уно

набор радиокомпонентов на 5 долларов.

двухполярная dc-dc повышайка для ОУ

модуль дисплея, модуль реле, модуль светодиод , уно прото шилд.

Красивая кнопка герметичная лифтовая, можно с подсветкой :)

Действия

На прото шилде собираем схему измерения тока и схему подачи шим на базе ОУ.

фото схему и фото добавлю чуть позже.

оцилографом проверяем корректность работы шим.

Up to 51A Amps = Duty cycle x 0.6 Duty cycle = Amps / 0.6

10%  6A 
20%  12A 
30%  18A

код программы буду благодарен за помощь.

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);  // адрес дисплея бывает 0x3F
int prevbutt =0;                   // переменная для кнопки
int regim=6;                       // переменная режим зарядки
int sostoyanie=1;                  //(состояние подключения от 1 до 5)
int higeside=0;                    //переменная пик вольтажа
int lowside=0;                     //минимум вольтажа
int tiks1,tiks2,tiks3,tiks4,tiks5; //переменные уменьшающие количество ошибок
int tikdisp=0;                     //переменная обновления дисплея
int sensorValue;                    // переменная в которую читаем показания вольтажа
const int analogInPin = A1;         //на каком порту мерять напряжение
float count = 0;                    //будет счетчик киловатт
const int buttonPin = 14;    // на каком порту кнопка
const int grePin =  11;      // на каком порту зеленый светодиод
const int redPin =  13;      // на каком порту красный светодиод
const int bluPin =  12;      // на каком порту голубой светодиод
const int pwmout =  10;      // на каком порту шим выход на ОУ
int buttonState = 0;         // переменная для кнопки
int outputValue = 0;        // исходящее значение  PWM шим на ОУ
void setup() {
//    Serial.begin(9600);         // раскоментировать для отстройки напряжений с аналогового порта
  lcd.init();                      // инициализация дисплея
  lcd.backlight();                 // включить подсветку
  pinMode(grePin, OUTPUT);         //назначение выходов
  pinMode(bluPin, OUTPUT);         //назначение выходов
  pinMode(redPin, OUTPUT);         //назначение выходов
  pinMode(14, INPUT_PULLUP);       //назначение выходов
 
  TCCR1A = TCCR1A & 0xe0 | 1;      ///установка режима шим на 10 выход 978 герц
  TCCR1B = TCCR1B & 0xe0 | 0x0b;   ///установка режима шим на 10 выход 978 герц
  
}

void loop() {

  buttonState = digitalRead(buttonPin);   // читаем состояние кнопки
 if (buttonState == HIGH) {               // если нажата
    if (prevbutt==0){                     // если предидущее отпущено
      prevbutt=1; delay (50);             // антидребезг
    
 if (sostoyanie ==1){regim++;regim++;tikdisp=500; if (regim>=18){regim=6;} lcd.clear();}  // при нажатии меняем режим зарядки только при статусе неподключено
 
  }} else {prevbutt=0;}                  // кнопка отпущена         
  
  if (regim==6) { analogWrite(10, 24);}//  выбор заполнения шим при выборе тока заряда 0,6*процент заполнения но минимум 10%
  if (regim==8) { analogWrite(10, 31);}//  выбор заполнения шим при выборе тока заряда
  if (regim==10){ analogWrite(10, 39);}//  выбор заполнения шим при выборе тока заряда
  if (regim==12){ analogWrite(10, 46);}//  выбор заполнения шим при выборе тока заряда
  if (regim==14){ analogWrite(10, 53);}//  выбор заполнения шим при выборе тока заряда
  if (regim==16){ analogWrite(10, 61);}//  выбор заполнения шим при выборе тока заряда

  
   tikdisp++; if(tikdisp>500){
/////////////////////////выводим на дисплей
lcd.setCursor(1,0); lcd.print("UA OPEN CHARGE");
if (sostoyanie ==1){lcd.setCursor(0,1); lcd.print("UAOC Not Connected    ");}
if (sostoyanie ==2){lcd.setCursor(0,1); lcd.print("UAOC Connected        ");}
if (sostoyanie ==3){lcd.setCursor(0,1); lcd.print("UAOC Charging         ");}
if (sostoyanie ==4){lcd.setCursor(0,1); lcd.print("UAOC Ventilation Req. ");}
if (sostoyanie ==5){lcd.setCursor(0,1); lcd.print("UAOC diod test failed ");}
lcd.setCursor(0,2); lcd.print("charg current:"); lcd.setCursor(14,2); lcd.print(regim);lcd.setCursor(17,2); lcd.print("A");
lcd.setCursor(0,3); lcd.print("count:");lcd.setCursor(7,3); lcd.print(count,1);lcd.setCursor(12,3); lcd.print("kWh");
//////////// моргаем светодиодом
digitalWrite(grePin, LOW);digitalWrite(bluPin, LOW);digitalWrite(redPin, LOW);
if (sostoyanie ==1){  digitalWrite(grePin, HIGH);}
if (sostoyanie ==2){  digitalWrite(grePin, HIGH);digitalWrite(redPin, HIGH);}
if (sostoyanie ==3){  digitalWrite(bluPin, HIGH);}
if (sostoyanie ==4){  digitalWrite(redPin, HIGH);}
if (sostoyanie ==5){  digitalWrite(redPin, HIGH);digitalWrite(bluPin, HIGH);}
tikdisp=0;}   
   
   
  sensorValue = analogRead(analogInPin);                  // читаем показания аналогового порта для выбора режима работы
 //   Serial.print("sensor = ");  Serial.println (sensorValue); // раскоментировать для отстройки напряжений
  if (sensorValue>500){higeside=sensorValue;}            // если показания больше 500 это пик 
  if (sensorValue<500){lowside=sensorValue; }            // если показания меньше 500 это низ 

  /////////////////выбор состояния подключения в зависимости от состояния на аналоговом порту защита от быстрой перемены режимов
  
  if (higeside<900                              ) {tiks5++;if (tiks5>=50){tiks5=50; }} else { tiks5 --;if (tiks5<=0){tiks5=0;};} 
  if (higeside>900&& lowside>420                ) {tiks4++;if (tiks4>=50){tiks4=50; }} else { tiks4 --;if (tiks4<=0){tiks4=0;};}
  if (higeside>900&& lowside>340 && lowside<420 ) {tiks3++;if (tiks3>=50){tiks3=50; }} else { tiks3 --;if (tiks3<=0){tiks3=0;};}
  if (higeside>900&& lowside>250 && lowside<340 ) {tiks2++;if (tiks2>=50){tiks2=50; }} else { tiks2 --;if (tiks2<=0){tiks2=0;};}
  if (higeside>900&& lowside<250                ) {tiks1++;if (tiks1>=50){tiks1=50; }} else { tiks1 --;if (tiks1<=0){tiks1=0;};}
    
     if (tiks5>=30){sostoyanie = 5;tikdisp=500;}      
     if (tiks4>=30){sostoyanie = 4;tikdisp=500;}      
     if (tiks1>=30){sostoyanie = 1;tikdisp=500;}
     if (tiks2>=30){sostoyanie = 2;tikdisp=500;}
     if (tiks3>=30){sostoyanie = 3;tikdisp=500;}
          

}

 

daniilguz
Offline
Зарегистрирован: 13.01.2020

добавки к проекту схемы распайки модулей.

если вы покупаете модули то первые две схемы Вам не нужны. Только последняя для подачи шим на вывод авто.

модуль лэд

организация шим сигнала

b707
Offline
Зарегистрирован: 26.05.2017

daniilguz пишет:

код программы буду благодарен за помощь.

пока не понятно, какая помощь требуется

daniilguz
Offline
Зарегистрирован: 13.01.2020

Для зарядки электромобиля используется разъем стандарта J1772.

Судя по описанию данный разъем имеет следующие контакты:

1-фаза, 2-ноль, 3-заземление+GND, 4-пилотный сигнал, 5 пока не разобрался, чаще не задействован.

На разъем зарядного пистолета через резистор 1 кОм (1%) нужно подать шим сигнал частотой 1000 герц   от -12 до +12 вольт, те амплитудой 24 вольта.

Ток заряда регулируется степенью заполнения шим на данном проводе.

То есть для подачи шим от ардуино можно использовать операционный усилитель, для питания ОУ оспользуется dc-dc повышайка  с двухполярным выходом для питания ОУ.

Автомобиль имеет встроенный диод, резистор на 2,7 кОм и подключаемый резистор на 1,3 кОм для изменения напряжения на разъеме.

поэтому ардуино меряет текущее напряжение на порту А1 для определения амплитуды напряжения для выбора режима подключения.

Пока пистолет висит в воздухе напряжение на пилоте 12 вольт.

Когда выполнилось подключение на разъеме 9 вольт (резистор 2,4 кОм + диод) вольтаж 9 В.

Когда идет заряд подключается резистор 1,3К параллельно 2,7К  (режим зарядки) вольтаж 6 В.

Когда нажата кнопка на пистолете подключается резистор 300 R заряд прекращается вольтаж 3В.

Когда выключено питание вольтаж 0  (не пойму зачем этот режим).

когда нет диода напряжение -12 вольт.

оригинальные описания режимов:

State     Pilot Voltage           EV Resistance       Description 
State A        12                             N/A                      Not Connected 
State B         9                             2.74k                   Connected 
State C         6                              882                     Charging 
State D         3                              246                     Ventilation Required 
State E         0                               N/A                     No power 
State F       -12                              N/A                     EVSE Error

Степень заполнения шим рассчитывается по формуле

0,6*степень заполнения = ток заряда.

Степень заполнения не может быть меньше 10 %

оригинальная статья без перевода

Up to 51A Amps = Duty cycle x 0.6 Duty cycle = Amps / 0.6
51 - 80A Amps = (Duty Cycle - 64) 2.5
Duty Cycle  Max Current 
< 3%  Error 
3% - 7%  Digitial Com Required 
10%  6A 
20%  12A 
30%  18A 
40%  24A 
50%  30A

по несущей частоте стандарт имеет ограничения от 980 до 1020 герц

К сожалению не пойму как сделать частоту ровно 1000, поэтому пока оставляю несущую частоту 976 герц.

на порты 11,12,13 я подключил лед модуль.

на порт 8 подключу модуль реле ( пока нет в программе)

с порта 10 будем подавать шим на операционный усилитель.

на порт D14 (А0 ) у меня подключена кнопка.

но порт А1 измерительная цепь напряжения.

а4, а5 , дисплей.

daniilguz
Offline
Зарегистрирован: 13.01.2020

помогите пож. сделать несущую частоту ШИМ в пределах стандарта 980-1020 гц.

sadman41
Offline
Зарегистрирован: 19.10.2016
 
Uno, Nano, Mini
 
pins 3, 5, 6, 9, 10, 11: 490 Hz 
pins 5 and 6: 980 Hz
daniilguz
Offline
Зарегистрирован: 13.01.2020

Проверю спасибо, пока только меньше 980 получалось.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

daniilguz пишет:

помогите пож. сделать несущую частоту ШИМ в пределах стандарта 980-1020 гц.

Вот здесь есть пример как делать ШИМ произвольной частоты.

Igor_116
Offline
Зарегистрирован: 09.11.2017

эта ветка для оконченных  проектов.

daniilguz
Offline
Зарегистрирован: 13.01.2020

Спасибо все получилось теперь все в пределах стандарта.

daniilguz
Offline
Зарегистрирован: 13.01.2020

Зарядка создана окончательный код ниже.

Будут вопросы пишите.

Пока жду пистолет, вид конечного устройства скину немного позже.


#include <LiquidCrystal_I2C.h>
#include   <EEPROM.h>

LiquidCrystal_I2C lcd(0x27,20,4);  // адрес дисплея бывает 0x3F
  unsigned long currentTime;       //тайминг
  unsigned long loopTime;          //тайминг
  unsigned long currentTime2;      //тайминг
  unsigned long loopTime2;         //тайминг
int prevbutt =1;                   // переменная для кнопки
int regim=EEPROM.read(1);;          // переменная режим зарядки
int prevregim=1;
int sostoyanie=5;                  //(состояние подключения от 1 до 5)
int prevsost=5;                    //(состояние подключения от 1 до 5)
int higeside=0;                    //переменная пик вольтажа
int lowside=0;                     //минимум вольтажа
int tiks1,tiks2,tiks3,tiks4,tiks5; //переменные уменьшающие количество ошибок
int tikdisp=0;                     //переменная обновления дисплея
int sensorValue;                    // переменная в которую читаем показания вольтажа
const int analogInPin = A1;         //на каком порту мерять напряжение
float count = 0;                    //будет счетчик киловатт
const int buttonPin = 14;    // на каком порту кнопка
const int grePin =  11;      // на каком порту зеленый светодиод
const int redPin =  13;      // на каком порту красный светодиод
const int bluPin =  12;      // на каком порту голубой светодиод
const int pwmout =  10;      // на каком порту шим выход на ОУ
const int relePin=  3

;       // на каком порту шим выход на ОУ
int buttonState = 0;         // переменная для кнопки
int outputValue = 0;         // исходящее значение  PWM шим на ОУ
int flagt;
void setup() {
    Serial.begin(9600);          // раскоментировать для отстройки напряжений с аналогового порта
  lcd.init();                      // инициализация дисплея
  lcd.backlight();                 // включить подсветку
  pinMode(grePin, OUTPUT);         //назначение выходов
  pinMode(bluPin, OUTPUT);         //назначение выходов
  pinMode(redPin, OUTPUT);         //назначение выходов
  pinMode(relePin, OUTPUT);        //назначение выходов
  pinMode(14, INPUT_PULLUP);       //назначение выходов
    pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  
  cli();
  TCCR1A = bit(COM1A0) | bit(COM1A1) | bit(COM1B1);
  TCCR1B = bit(WGM13) | 3;
  ICR1 = 125;
  OCR1A = OCR1B = 25;
  sei();
  
}

void loop() {

  buttonState = digitalRead(buttonPin);   // читаем состояние кнопки
 if (buttonState == HIGH) {               // если нажата
    if (prevbutt==0){                     // если предидущее отпущено
      prevbutt=1; delay (50);             // антидребезг
    
 if (sostoyanie ==1){regim++;regim++; flagt=0; if (regim>=18){regim=6;} lcd.clear();}  // при нажатии меняем режим зарядки только при статусе неподключено
   }} else {prevbutt=0;}                  // кнопка отпущена         
  
  if (regim==6) { if (prevregim!=6 ) {prevregim=6 ;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда 0,6*процент заполнения но минимум 10%
  if (regim==8) { if (prevregim!=8 ) {prevregim=8 ;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда
  if (regim==10){ if (prevregim!=10) {prevregim=10;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда
  if (regim==12){ if (prevregim!=12) {prevregim=12;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда
  if (regim==14){ if (prevregim!=14) {prevregim=14;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда
  if (regim==16){ if (prevregim!=16) {prevregim=16;   OCR1A = OCR1B = regim*2;EEPROM.write(1, regim) ;}}//  выбор заполнения шим при выборе тока заряда

  

   
   
  sensorValue = analogRead(analogInPin);                  // читаем показания аналогового порта для выбора режима работы
    Serial.print("sensor = ");  Serial.println (regim); // раскоментировать для отстройки напряжений
  if (sensorValue>500){higeside=sensorValue;}            // если показания больше 500 это пик 
  if (sensorValue<500){lowside=sensorValue; }            // если показания меньше 500 это низ 

  /////////////////выбор состояния подключения в зависимости от состояния на аналоговом порту защита от быстрой перемены режимов
  
  if (lowside>250                               ) {tiks5++;if (tiks5>=15){tiks5=15; }} else { tiks5 --;if (tiks5<=0){tiks5=0;};} 
  if (higeside<680&& lowside<250 &&higeside>580 ) {tiks4++;if (tiks4>=15){tiks4=15; }} else { tiks4 --;if (tiks4<=0){tiks4=0;};}
  if (higeside<780&& lowside<250 &&higeside>680 ) {tiks3++;if (tiks3>=15){tiks3=15; }} else { tiks3 --;if (tiks3<=0){tiks3=0;};}
  if (higeside<880&& lowside<250 &&higeside>780 ) {tiks2++;if (tiks2>=15){tiks2=15; }} else { tiks2 --;if (tiks2<=0){tiks2=0;};}
  if (higeside>880&& lowside<250                ) {tiks1++;if (tiks1>=15){tiks1=15; }} else { tiks1 --;if (tiks1<=0){tiks1=0;};}
    
     if (tiks5>=10){sostoyanie = 5;if(prevsost!=5){prevsost=5;flagt=0;}}     
     if (tiks4>=10){sostoyanie = 4;if(prevsost!=4){prevsost=4;flagt=0;}}      
     if (tiks1>=10){sostoyanie = 1;if(prevsost!=1){prevsost=1;flagt=0;}}
     if (tiks2>=10){sostoyanie = 2;if(prevsost!=2){prevsost=2;flagt=0;}}
     if (tiks3>=10){sostoyanie = 3;if(prevsost!=3){prevsost=3;flagt=0;}}
       currentTime  = millis(); // считываем время, прошедшее с момента запуска программы
       currentTime2  = millis(); // считываем время, прошедшее с момента запуска программы
   /////////////////////////////////////////////////////////быстрая часть
 
if(currentTime > (loopTime + flagt))
 {
      
/////////////////////////выводим на дисплей
lcd.setCursor(1,0); lcd.print("UA OPEN CHARGE");
if (sostoyanie ==1){lcd.setCursor(0,1); lcd.print(" Not Connected   ");}
if (sostoyanie ==2){lcd.setCursor(0,1); lcd.print(" Connected       ");}
if (sostoyanie ==3){lcd.setCursor(0,1); lcd.print(" Charging        ");}
if (sostoyanie ==4){lcd.setCursor(0,1); lcd.print(" Vent. Req       ");}
if (sostoyanie ==5){lcd.setCursor(0,1); lcd.print(" Diod test failed");}
lcd.setCursor(0,2); lcd.print("charg current:"); lcd.setCursor(14,2); lcd.print(regim);lcd.setCursor(17,2); lcd.print("A");
//lcd.setCursor(0,3); lcd.print("count:");lcd.setCursor(9,3); lcd.print(count,3);lcd.setCursor(13,3); lcd.print("kWh");
//////////// моргаем светодиодом
digitalWrite(grePin, LOW);digitalWrite(bluPin, LOW);digitalWrite(redPin, LOW);
if (sostoyanie ==1){  digitalWrite(grePin, HIGH);}
if (sostoyanie ==2){  digitalWrite(grePin, HIGH);digitalWrite(redPin, HIGH);}
if (sostoyanie ==3){  digitalWrite(bluPin, HIGH);}
if (sostoyanie ==4){  digitalWrite(redPin, HIGH);}
if (sostoyanie ==5){  digitalWrite(redPin, HIGH);}
if (sostoyanie ==3){  digitalWrite(relePin, HIGH);}else {digitalWrite(relePin, LOW);}
tikdisp=0;  
   
   loopTime= currentTime;
   flagt=10000;            //такт прохода
 }
// if(currentTime2 > (loopTime2 + 10000)) { if (sostoyanie ==3){count=count+(regim*0.000611094);}loopTime2= currentTime2; }         

}

 

b707
Offline
Зарегистрирован: 26.05.2017

"окончательный код" ? - кровь из глаз... Может вы хотели сказать  "черновик" ?

Вместо пяти -шести одинаковых условий используйте switch - case. ... А на месте быстрого накручивания переменных tiks3 и подобное...  может просто два раза прочитать порт с небольшой задержкой?

В строке 96 грубая ошибка, время так считать нельзя

daniilguz
Offline
Зарегистрирован: 13.01.2020

Пожалуй черновик.
Все поправлю.
Только в строка 96 не понял почему нельзя?

b707
Offline
Зарегистрирован: 26.05.2017

daniilguz пишет:
Только в строка 96 не понял почему нельзя?

читайте

http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis