Arduino MPPT SCC

This is the resources page for my Arduino MPPT project:

Here’s the schematic (click it to see full size version):

MPPTschematic

Here’s the main Arduino code (copy and save as ArduMPPT.ino):

// 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("MuPPeT",0,1);
LcdString("%",71,1);
LcdString("V",31,2);
LcdString("V",71,2);
LcdString("A",31,3);
LcdString("A",71,3);
LcdString("W",26,4);
LcdString("Wp",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,2); //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,3); //display the panelAmps

panelWatts = panelVolts * panelAmps; //calculate the watts
LcdString(dtostrf(panelWatts,4,1,&tmp[2]),0,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",41,0); //display an A for auto
}
}
else {
if (!mpptOn) {
pwmWrite(3, sensorValue / 4);
}
else {
mpptOn = false;
LcdString("M",41,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]),40,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]),40,3); //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]),50,1); //display the efficiency

maxwatts = max(maxwatts, panelWatts); //calculate the max watts
LcdString(dtostrf(maxwatts,4,1,&tmp[3]),40,4); //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 > 40) { //if in upper right quadrant
LcdPlot (chartX, chartY);
}

delay(10);
}

And here’s the header file containing the font array (save as header.h):

#include 



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 →
};

5 thoughts on “Arduino MPPT SCC

  1. Hi Julian,

    I am following your video blogs for quite a while now and love your detailed explanations. But you’re bringing them over that it never gets boring.
    However, I was planning to build my own MPPT charge controller and I got four 180W panels for free a long time ago. But I was too busy and ordering all the needed parts from the expensive stores around here was not a thing that made it easier to get back to some soldering at rainy evenings.

    With programming a lot of embedded things I got some better understanding of how china works, and then you explained all these little gimmicks, bits and pieces you ordered from there. So I gave it a chance and started to order some of these things over there myself. Hopefully they arrive in a few days.

    My Solar installation is quite a bit special, as I have to install the panels some meters away from the house, but I’d like to use the energy right in the house… May be we could exchange some ideas. You have my email account so if you like, join me on hangouts or write me an email. Would be great to hear from you.

    Astralix

  2. Hi Julian!
    I repeated your design, replace the battery (i use Pump Charger on NE555). However, good results could be achieved by PWM frequency at 1kHz! No more).
    With greater frequency produced no clear fronts – MOSFET strongly heated.
    I think the problem in switching speed PC817.
    What am I doing wrong?

    1. Hi Oleg,

      I’ve also repeated the design. I’m having some diffictulties with getting correct AMP-readings below 1 Amp. And since I’m using a 20W / max 1.2A solar panel, that’s pretty inconvenient. I’ll try using a resistor to measure the current, they’re ordered but haven’t arrived yet. I’ve also tried using a 470nF condensator on the 712, but I’m getting _a lot_ of noise.

      About your problem: I’m using an IRF530 mosfet instead, my local supplied didn’t have the specified ones, but they seem to work fine. I’ve tested both the standard frequency of 500hz and also 32k (using a cheap arduino nano immitation). With 32k, I first got very fuzzy fronts too, but then I put a bit of load on the mosfet and the fronts looked quite good. Of course, without the coil or the big condensators attached that are located “behind” the mosfet. So I don’t think there’s a problem with the PC817.

      However, I’m no expert, I got help in a local hacker space where we used an old analogue oscilloscope to check it out and it looked fine.

      Have you tried using a 9V Block battery to eliminate the possibility, that the charge pump is causing the issue?

      Besides my issues with measuring the current, MPPT seems to work – though my design has some flaws .. I’ve put the coil too far away from the diode and the mosfet, so I’m having some resistance in critical current paths. I’m pretty much burning more energy into heat than gaining, even when doing manual MPPT. For the moment, I’m running the circuit as a PWM-Charger, only making sure, that the outgoing voltage is not going above 14.4V which works pretty well. The efficiency is good too in that mode, not much heat loss. Also, I’m not using Julian’s Code. I wanted to implement a simple MPPT-Algorithm myself, but as far as I can tell, my heat loss issues have nothing to do with the software but with some wrong choices when placing the components.

      @Julian, this has been a fun project, your design and all of your videos are greatly appreciated. I’ve had an accident, broke my arm had got wayyy to much time on my hand(s). This really helped me and gave me something somewhat useful to do!

      Best regards,
      Mark

      1. Hi, moztr! (May be correctly, Mark)
        In last time Julian interested in Chinese KITs and completely abandoned the solar charge controller :(
        Can I directly contact you? There are several issues and there is a story to tell :)
        My last result (PWM only) you can see http://robocraft.ru/blog/3413.html
        And now i testing other variants. I think you would be interested.

        Yours sincerely, Oleg

Leave a Reply to moztr Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>