Julian on February 25th, 2014

Final Production Run of the PWM5

I’m taking a break from manufacturing the PWM5 Solar Charge Controller, so I’ve closed the shop. This is so that I can spend more time building my YouTube channel. If you have contacted me recently about purchasing the PWM5, there are parts in stock for about 50 more units and I will be assembling them over the next few weeks/months.

Update – 4th March: Cancelling this project has generated a fair amount of interest in the PWM5. There is now a waiting list for about 40 units. I will be in touch with everyone who has emailed or left comments on this site, but this will take time, possibly months rather than weeks. I’ll tweet progress reports on manufacturing @julian256 (or see the tweet panel to the right). Prices remain at £20 UK, €28 Europe and $40 International.

Julian on March 8th, 2014

I’ve put a higher resolution image of the hand-drawn schematic up now. In the comments below, Tima noticed a resistor in the video connected between Gate and Source of the MOSFET. This is a 10M ohm resistor which I added to protect the gate against stray voltage while I was assembling the breadboard – it’s not critical, can be included or left out.

The code listing (below) requires PWM.h - this comes from the library “PWM Frequency Library” which you’ll have to install.
Have a look at my video: Arduino PWM Tutorial #2 – How to Set PWM Frequency Accurately http://youtu.be/9JXGIeM3BSI
MPPTschematic

Julian on March 1st, 2014

I’m now making the last 50 PWM5 Solar Charge Controllers. This is a panel of 28 printed circuit boards. There are about 32 components on the board including 3 surface mount devices on the underside. Fitted first are most of the charge pump components including the 1N4148 diodes and the 47nF ceramic capacitors. Also fitted at this time is the 5mm blue LED.

IMG_20140301_165251

Julian on February 28th, 2014

PWM5 Circuit DiagramUntil recently I hadn’t drawn a complete circuit diagram for the PWM5 Solar Charge Controller, just a few sketches of the various circuit elements which are scattered throughout the website. So now here’s the whole thing including doodles and inkblots (click the image for a larger picture).

Note that Vbatt appears in two different locations on the drawing and 0v (zero volts) in a number of positions along the bottom. Vgate is the high voltage generated by the charge pump.

There’s a really useful variant of the LM2596 voltage regulator called the LM2596HV which can handle 60 volts on its input. But be careful – some that I bought recently turned out to be fake. At about 50v, the regulator became unstable and died which could very easily destroy whatever you have connected to the output.

Using the Dual Complementary Opto-Isolator (DCOI) MOSFET driver to interface a microcontroller with high-side and low-side, N channel and P channel MOSFETs.

Julian on January 2nd, 2014

energysavingled.com sent me this 7W GU10 LED spotlight for review. I was interested primarily in the safety aspects of this product.

Julian on October 15th, 2013

When I started designing the PWM5, all my scribblings and jottings were posted as entries on this website. Now, however, I’m increasingly using YouTube to document the design process. As an example, here is the latest in my video series on a new Arduino-based MPPT solar charge controller.

But YouTube has no mechanism for providing supplementary information like schematics and code listings, so I’ve decided to post these as entries on this site.

#include <Arduino.h>



static const byte ASCII[][5] =
{
 {0x00, 0x00, 0x00, 0x00, 0x00} // 20  
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥
,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j 
,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ←
,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f →
};

Julian on October 14th, 2013
// Wiring:
//A0 - Pot
//A1 - Volts (solar)
//A2 - Volts (battery)
//A4 - Amps (solar)
//A5 - Amps (battery)

//D3 - PWM out to DCOI
//D8 -LCD_RST (1)
//D9 - LCD_CE (2)
//D10 - LCD_DC (3)
//D11 - LCD_DIN (4)
//D12 - LCD_CLK (5)
//-----LCD_VCC (6)
//---LCD_LIGHT (7)
//-----LCD_GND (8)

#define LCD_C     LOW
#define LCD_D     HIGH

#define PIN_RESET 8
#define PIN_SCE   9
#define PIN_DC    10
#define PIN_SDIN  11
#define PIN_SCLK  12



//#include <stdlib.h>;
#include "header.h";
#include <PWM.h>
int32_t frequency = 15000; //frequency (in Hz)


int sensorValue = 0;
float panelVolts = 0;
float batteryVolts = 0;
float lastPanelVolts = 0;
float lastBatteryVolts = 0;
float panelAmps = 0;
float lastPanelAmps = 0;
float batteryAmps = 0;
float lastBatteryAmps = 0;
float efficiency = 0;
float siemens = 0;
float panelWatts = 0;
float lastPanelWatts = 0;
float batteryWatts = 0;
float maxwatts = 0;
int barwatts = 0;
float Voc = 0;
float Isc = 0;
char tmp[25];
//boolean gotVoc = false;
//boolean gotIsc = false;
boolean highWatts = false;
int chartX = 0;
int chartY = 0;
//int bin = 0;
boolean mpptOn = false;
const boolean initialise = true;

void LcdInitialise(void)
{
  pinMode(PIN_SCE, OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC, OUTPUT);
  pinMode(PIN_SDIN, OUTPUT);
  pinMode(PIN_SCLK, OUTPUT);
  digitalWrite(PIN_RESET, LOW);
  digitalWrite(PIN_RESET, HIGH);
  LcdWrite(LCD_C, 0x21 );  // LCD Extended Commands.
  LcdWrite(LCD_C, 0xBC );  // Set LCD Vop (Contrast). 
  LcdWrite(LCD_C, 0x04 );  // Set Temp coefficent. //0x04
  LcdWrite(LCD_C, 0x14 );  // LCD bias mode 1:48. //0x13
  LcdWrite(LCD_C, 0x0C );  // LCD in normal mode.
  LcdWrite(LCD_C, 0x20 );
  LcdWrite(LCD_C, 0x0C );
}

void LcdClear()
{
  for (int i=0; i<504; i++) LcdWrite(LCD_D, 0x00);
}

void LcdWrite(byte dc, byte data)
{
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}

void LcdString(char *characters, int x, int y)
{
  LcdWrite(LCD_C, 0x80 | x);  // Column.
  LcdWrite(LCD_C, 0x40 | y);  // Row.
  while (*characters) LcdCharacter(*characters++);
}

void LcdCharacter(char character)
{
  for (int index = 0; index < 5; index++) LcdWrite(LCD_D, ASCII[character - 0x20][index]);
  LcdWrite(LCD_D, 0x00);
}

void LcdXY(int x, int y)
{
  LcdWrite(LCD_C, 0x80 | x);  // Column.
  LcdWrite(LCD_C, 0x40 | y);  // Row.
}

void LcdPlot (int x, int y)
{
//  static int lastX;
//  static int lastY;
  LcdXY(x,5-(y/8)); //set display address for plot
  int bin=128;
  for (int q=0; q<y%8; q++) bin=bin/2; //calculate pixel position in byte
  LcdWrite(LCD_D, bin); //plot pixel
  float slope=float(47-y)/float(83-x);
  
  for (int j=x+2; j<84; j++) {
    float dy=slope*float(j-x);
    int k=y+round(dy);
    LcdXY(j,5-(k/8)); //set display address for plot
    int bin=128;
    for (int q=0; q<k%8; q++) bin=bin/2; //calculate pixel position in byte
    LcdWrite(LCD_D, bin); //plot pixel
  }
//  lastX = x;
//  lastY = y;
}

void perturb(boolean init=false)
{
  static byte pulseWidth = 0;
  static boolean trackDirection = false; //counts down / pwm increases
//  static int loopCounter = 0;
  if (init) {
    pulseWidth = 255;
    trackDirection = false;
  }
  else {
    if (!trackDirection) {
      if (pulseWidth != 0) {pulseWidth--;} else {trackDirection = true;}
    }
    else {
      if (pulseWidth != 255) {pulseWidth++;} else {trackDirection = false;}
    }
  }
  pwmWrite(3, pulseWidth); //write perturbed PWM value to PWM hardware
  if ((panelWatts - lastPanelWatts) < -0.1) trackDirection = !trackDirection;
}

void setup()
{
  LcdInitialise();
  LcdClear();
  LcdString("Solar",0,0);
  LcdString("MPPT",3,1);
  LcdString("B",31,2);
  LcdString("V",31,3);
  LcdString("Wp",65,3);
  LcdString("A",31,4);
  LcdString("W",65,4);
  
  InitTimersSafe(); 
  bool success = SetPinFrequencySafe(3, frequency);
  if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);    
  }

}




void loop()
{
  panelVolts = analogRead(A1) * 47.0 / 1023; //get the volts
  panelVolts = (panelVolts + lastPanelVolts) / 2; //average the volts
  lastPanelVolts = panelVolts;
  LcdString(dtostrf(panelVolts,5,2,&tmp[0]),0,3); //display the volts
  
  panelAmps = (514 - analogRead(A4)) * 27.03 / 1023; //get the panelAmps
  panelAmps = (panelAmps + lastPanelAmps) / 2; //average the panelAmps
  lastPanelAmps = panelAmps;
  if (panelAmps < 0) panelAmps = 0; //don't let the panelAmps go below zero
  LcdString(dtostrf(panelAmps,5,2,&tmp[1]),0,4); //display the panelAmps
  
  panelWatts = panelVolts * panelAmps; //calculate the watts
  LcdString(dtostrf(panelWatts,4,1,&tmp[2]),40,4); //display the panel watts

  sensorValue = analogRead(A0);
  if (sensorValue > 1020) {
//    pwmWrite(3, sensorValue / 4);
    if (mpptOn) {
      perturb();
    }
    else {
      mpptOn = true;
      perturb(initialise); //initialise the perturb algorithm
      LcdString("A",35,0); //display an A for auto
    }
  }
  else {
    if (!mpptOn) {
      pwmWrite(3, sensorValue / 4);
    }
    else {
      mpptOn = false;
      LcdString("M",35,0); //display an M for manual
    }
  }
  
  lastPanelWatts = panelWatts;
  
  batteryVolts = analogRead(A2) * 25.0 / 1023; //get the battery volts
  batteryVolts = (batteryVolts + lastBatteryVolts) / 2; //average the volts
  lastBatteryVolts = batteryVolts;
  LcdString(dtostrf(batteryVolts,5,2,&tmp[0]),0,2); //display the battery volts
  
  batteryAmps = (514 - analogRead(A5)) * 27.03 / 1023; //get the panelAmps
  batteryAmps = (batteryAmps + lastBatteryAmps) / 2; //average the panelAmps
  lastBatteryAmps = batteryAmps;
  if (batteryAmps < 0) batteryAmps = 0; //don't let the batteryAmps go below zero
//  LcdString(dtostrf(batteryAmps,5,2,&tmp[1]),0,4); //display the panelAmps
  
  
//  if (volts > 1) {
//    siemens = panelAmps / volts; //calculate the conductance
//    LcdString(dtostrf(siemens,5,2,&tmp[2]),0,2); //display the siemens
//  }
  
  
  batteryWatts = batteryVolts * batteryAmps; //calculate the battery watts

  efficiency = batteryWatts / panelWatts * 100;
  LcdString(dtostrf(efficiency,3,0,&tmp[2]),0,1); //display the efficiency
  
  maxwatts = max(maxwatts, panelWatts); //calculate the max watts
  LcdString(dtostrf(maxwatts,4,1,&tmp[3]),40,3); //display the max watts
  
  
  if (panelWatts > 27) highWatts = true; //go to 83Watt bargraph
  
  
  LcdXY(0,5);
    if (!highWatts) {
      barwatts = round(panelWatts * 3);
      for(int k=0; k<84; k++)
      {
        if (k >= barwatts) LcdWrite (LCD_D, 0x00);
        else if (k % 30 == 0) LcdWrite (LCD_D, 0xf0);
        else if (k % 15 == 0) LcdWrite (LCD_D, 0xe0);
        else LcdWrite (LCD_D, 0xc0);
      }
    }
    else {
      barwatts = round(panelWatts);
      for(int k=0; k<84; k++)
      {
        if (k >= barwatts) LcdWrite (LCD_D, 0x00);
        else if (k % 10 == 0) LcdWrite (LCD_D, 0xf0);
        else if (k % 5 == 0) LcdWrite (LCD_D, 0xe0);
        else LcdWrite (LCD_D, 0xc0);
      }
    }
  
  
  Voc = max(Voc, panelVolts); 
  Isc = max(Isc, panelAmps);
  
  
  chartX = min(panelVolts/Voc*84, 83);
  chartY = min(panelAmps/Isc*48, 47);

  if (chartX > 41 & chartY > 23) { //if in upper right quadrant
    LcdPlot (chartX, chartY);
  }
  
  
  delay(10);
}

Google+