Monday, September 2, 2013

The vision of Ubi Vis

The purpose of this little project of mine is to get charging stations everywhere!

We need charging stations on every lamp post in every parking lot in every retailed, public building and sky scrapper parking garage.  They just need to be everywhere all the time.  The only hindrance is our will to make it happen.

What about a program to offer free charging stations to anyone who wanted them, as many as they want!  Why not a 2D barcode on the box and do it honor system?  I know it sounds naive, I don't care.

The Design

I finalized the software and hardware to control the relay.  This is the heart of the charger and it means I am very close to an operational unit, I just need to plug it all together.  When I created the sense amplifier, I used an PNP (2N3906) transistor instead of an NPN (2N3904), they where mixed in the same small bag and very faintly written. That got me very confused for a long while.  Once I replaced them... wham it all worked first try.  Very happy!




It works perfect fine on 120 or 240vac.  If it senses 120vac, it requests that the car not pull any more than 10A.  On 240vac, it asks for 30A.

/*
  J1772/EVSE Lite
  (c) 2013 Bruce Meacham

  Used to control the charging of an Electric Vehicle from a DigiSpark ATtiny85/Arduino controller
 */

#define acInputPin       0  //P0 -- INPUT: AC->DC input.  Used to figure out if we're charging on 120vac or 240vac
#define currentSelectPin 1  //P1 -- INPUT: Switch, off(open) is "low current", on(closed) is "high current"
#define pilotSensePin    1  //P2 -- INPUT: Analog sense input, used to determine if the car is connected and wants to be charged
#define pilotPWMPin      4  //P4 -- OUTPUT: 1khz PWM duty cycle tells car requested current (see J1772 spec)
#define relayPin         5  //P5 -- OUTPUT: Connects to a transistor that will turns on the relay to energize the L1/L2

// ___INVERTER__
#define lowCurrentDuty  42  // 10 Amps (16.6%) -> 256 * 10A / 60 (it's inverted)
#define highCurrentDuty 128 // 30 Amps (50%) -> 256 * 30A / 60

// Counters used to determine if the car wants to charge state
int countGoHot = 0;
int totalCount = 0;

// Minimum cut-off, used to determine if we're even minimialy functional
#define pilotLowCutOff 10
//  0v - error/broken
//  12v (4.5v after splitter) not plugged in
//  9v  (3.6v after splitter) plugged into vehicle
//  6v  (2.4v after splitter) vehicle ready to charge
//  3v  (1.2v after splitter) vehicle ready to charge, vent required
//  7.5v is the mid-point x-over to "ready to chage" (3.2v after splitter) =>  652
#define pilotHighCutOff 652

#define cyclesToCheck 200
#define hotThreshold ((cyclesToCheck) / 10)  // 10% of the highs need to be in this range
#define minAC      40    // defines the minumum value for the 120vac range
#define maxAC120   500   // defines the maximum value for the 120vac range

void setup()  {
  // Initialize the PWM signal for pilot
  analogWrite(pilotPWMPin, lowCurrentDuty);        

  // Setup the digital I/O ports -- not analog
  pinMode(relayPin, OUTPUT);  
  digitalWrite(relayPin, 0);   // set it high (it's inverted)
  pinMode(currentSelectPin, INPUT);
}

void loop()  {
  // Sense the pilot and figure out if which range we're in
  //  0v - error/broken
  //  12v (4.5v after splitter) not plugged in
  //  9v  (3.6v after splitter) plugged into vehicle
  //  6v  (2.4v after splitter) vehicle ready to charge
  //  3v  (1.2v after splitter) vehicle ready to charge, vent required
  int pilotSense = analogRead(pilotSensePin); 
  if (pilotSense > pilotLowCutOff && pilotSense < pilotHighCutOff)
    countGoHot++;

  totalCount++;

  if (cyclesToCheck == totalCount)
  {
    // set the relay hot if we had some lows (bottom of the phase) and very few highs (not plugged or not ready)
    // the relay circuit is inverted
    if (countGoHot > hotThreshold)
      digitalWrite(relayPin, 1);
    else
      digitalWrite(relayPin, 0);
    totalCount = 0;
    countGoHot = 0;
   
    // Read the current select switch and set the duty cycle accordingly (0-Low Current, 1-High Current)
    byte currentSelect = digitalRead(currentSelectPin);
    int acIn = analogRead(acInputPin);
    byte duty = (0 == currentSelect /*|| (acIn > minAC && acIn < maxAC120)*/) ? lowCurrentDuty : highCurrentDuty;

    analogWrite(pilotPWMPin, duty);        
  }
}