This study is an example how to build a 3-phases 230V/380V from a 12-24V DC with an Arduino Uno
Things used in this project
Hardware components
Hand tools and fabrication machines
Digilent Mastech MS8217 Autorange Digital Multimeter
Soldering iron (generic)
Story
The story begins with an ugly device bought from China that was supposed to be an 230V inverter. But it was only a kind of high voltage generator, at the frequency of 20KHz. Absolutely nothing to regulate the output voltage, and no 50Hz to make supposing a AC use of this high voltage.
So I kept the high frequency transformer and I decided to build something with it.
Custom parts and enclosures
inverter manual
Schematics
full inverter diagram
Code
inverter code
Arduino
/*
Mini inverter for a 230Vac @ 50Hz from a 12V ro 24 DC, for a power up to 500W.
_________________________________________________________________
| |
| author : Philippe de Craene <[email protected] |
| Free of use - Any feedback is welcome |
_________________________________________________________________
The converter works in 2 parts:
- First the DC input is transformed to 220V/400VDC with a DC-DC converter based on
Timer2 31KHz. Timer2 works with Arduino pins 3 and 11.
- Then the high voltage is cut in a sequence 6x 3.33ms (300Hz x6 = 50Hz) from interrupts set by Timer1,
in order to make 3 phases AC sqare signal. Between 2 phases to result is a pseudo sinus signal, enough for a motor.
PWM mode explanation: https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
Timer 1 register access from: https://github.com/PaulStoffregen/TimerOne
SPWM code from: https://github.com/Irev-Dev/Arduino-Atmel-sPWM
remarks:
--------
The _BV(XXX) function sets the XXX bit of whatever register you are working with to one.
It is defined by the following C macro buried somewhere in the libraries used by the compiler:
#define _BV(bit) (1 << (bit))
Arduino Uno pinout
------------------
A0 ==> AC output rectified voltage sensor
A1 ==> DC input current sensor ACS712 20A
A2 ==> heatsink temperature sensor LM35
A3 ==> battery input voltage
A4 ==> LCD 1602 SDA
A5 ==> LCD 1602 SCL
4 ==> disable: SD pin of IR2184 (AC output driver)
8 ==> DC to AC phase 1 with IR2184
9 ==> DC to AC phase 2 with IR2184
10 ==> DC to AC phase 3 with IR2184
3 ==> 12VDC to 400VDC with IR2011
11 ==> 12VDC to 400VDC with IR2011
12 ==> synchro output (for oscilloscope external trigger)
13 ==> activity / alarm LED output
Versions history
----------------
version 0.5 - 20 july 2020 - first operational version
version 0.6 - 2 aug 2020 - 50Hz starts once Vht is raised
*/
#include <LiquidCrystal_I2C.h> // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
#include <TimerOne.h> // https://github.com/PaulStoffregen/TimerOne
// Parameters
//-----------
bool SETUP_MODE = false; // read the Ioffset value needed for 0 current
bool VERBOSE_MODE = true; // set the console display mode
const int Ioffset = 565; // set to get 0 when no current (mid value 0-1023
const float coefVht = 1.26; // calibrate the output 2 phases AC
const float coefVbat = 0.97; // calibrate the input DC voltage
const int microseconds = 3333; // AC period => 3.33ms for 300Hz = 6x 50Hz
const int VhtRef = 200; // 200V DC gives 192V AC
const int deltaVht = 4; // tolerance/hysteresis on Vht
const int VbatMin = 11; // inverter will stop below this input voltage
const int VbatMax = 29; // inverter will stop after this input voltage
bool isThsSensor = false; // is there or not a temperature sensor on IRFB4110 heatsink
const int ThsMax = 70; // heater sink max temperature
bool isIdcSensor = true; // is there or not a current sensor in use
const int IdcMax = 10; // absolute max current from DC input => 10 = 10A
// choose the right current sensor model for ACS712:
//float convI = 185.0; // 5A ACS712 module => 185mV/A
float convI = 100.0; // 20A ACS712 module => 100mV/A
//float convI = 66.0; // 30A ACS712 module => 66mV/A
// Hardware connexion
//-------------------
const byte pp01Pin = 3; // push-pull output 1 for 12VDC / 400VDC converter
const byte pp02Pin = 11; // push-pull output 2 for 12VDC / 400VDC converter
const byte ac1Pin = 8; // DC to AC phase 1
const byte ac2Pin = 9; // DC to AC phase 2
const byte ac3Pin = 10; // DC to AC phase 3
const byte enablePin = 4; // disable/enable AC output, enable to LOW
const byte triggerPin = 12; // ext trigger for oscilloscope
const byte ledPin = 13; // LED for activity and alarm
const byte VhtPin = 0; // analog 0 = output HT transformer sensor
const byte IdcPin = 1; // analog 1 = DC input current sensor ACS712 20A
const byte ThsPin = 2; // analog 2 = heatsink temperature sensor LM35
const byte VbatPin = 3; // analog 3 = input level voltage sensor
// Global variables
//-----------------
int HTduty = 1; // duty cycle for HT converter
const int HTdutyMax = 127; // must be below to 128 according to push-pull/bridge PWM
bool disable = true; // flag for enabling/disabling AC
unsigned int measures = 10; // number of readings for each measure of Vht Idc and Ths
unsigned int displayUpdate = 50; // number of measures cycles for LCD display update
// LCD declaration with I2C
//-------------------------
// set the I2C LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
// => pinup for I2C with Arduino Uno R3 : SDA = A4, SCL = A5
//
// setup
//____________________________________________________________________________________________
void setup() {
// define inputs & outputs
//------------------------
// do not set pinmode for analog entries, otherwise A3 causes a Timer2 error
pinMode( enablePin, OUTPUT ); digitalWrite(enablePin, LOW);
pinMode( ac1Pin, OUTPUT ); digitalWrite(ac1Pin, LOW);
pinMode( ac2Pin, OUTPUT ); digitalWrite(ac2Pin, LOW);
pinMode( ac3Pin, OUTPUT ); digitalWrite(ac3Pin, LOW);
pinMode( pp01Pin, OUTPUT ); digitalWrite(pp01Pin, LOW);
pinMode( pp02Pin, OUTPUT ); digitalWrite(pp02Pin, LOW);
pinMode( triggerPin, OUTPUT );
pinMode( ledPin, OUTPUT);
// LCD initialisation
lcd.begin(); // initialize the lcd for 16 chars 2 lines
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("INVERTER");
lcd.setCursor(0, 1);
lcd.print("is starting !");
// console initialisation
Serial.begin(250000);
Serial.println("Starting....");
// prepare timers
//---------------
// set Timer2 clock divider at 1 for a PWM frequency fixed to 31372.55 Hz
// Arduino Uno pins 3 and 11
// https://etechnophiles.com/change-frequency-pwm-pins-arduino-uno/
TCCR2B = _BV(CS10); // set clock at 31KHz
TCCR2A = _BV(COM2A1) // set non-inverting pp01Pin output 3
| _BV(COM2B1) // set non-inverting pp02Pin output 11
| _BV(COM2B0) // set inverting mode on
| _BV(WGM20); // fast PWM
OCR2A = HTduty; // set duty cycle <126 to prevent against both side continuity
OCR2B = 255 - HTduty; // the inverting ratio
// set Timer1 for an interrupt every 3333us
Timer1.initialize(microseconds);
Timer1.attachInterrupt(ACgenerate);
} // end of setup
//
// loop
//____________________________________________________________________________________________
void loop() {
float Idc; // DC input current
int Vbat, Vht, Ths; // Input voltage, HT output rectified voltage, heatsink temperature
static long cumulIdc = 0; // cumulative input DC current sensor
static long cumulVbat = 0; // cumulative input DC voltage sensor
static long cumulVht = 0; // cumulative output AC voltage sensor
static long cumulThs = 0; // cumulative heatsink temperature sensor
static int counter = 0; // cycles counter
static int counter2 = 0; // measures counter
// Idc DC input current sensor reading with overcurrent security
if( isIdcSensor ) {
Idc = analogRead(IdcPin); // 0 to 1023 in bytes for -20A to 20A, 512 in bytes =0A
delayMicroseconds(100);
if( Idc < 2 || Idc > 1022 ) { // reading in bytes
ACenable(2); // stop AC
return; // nothing else is done
}
} // end of test isIdcSensor
else Idc = Ioffset; // if no current sensor
// Vbat input volatge & Vht transformer output & Ths temperature reading
Vbat = analogRead(VbatPin); delayMicroseconds(100);
Vht = analogRead(VhtPin); delayMicroseconds(100);
if( isThsSensor ) { Ths = analogRead(ThsPin); delayMicroseconds(100); }
else Ths = 0;
// perform cumulative readings to improve accurancy against noises
cumulIdc += Idc;
cumulVbat += Vbat;
cumulVht += Vht;
cumulThs += Ths;
if( ++counter > measures ) { // after 'measures' number we go in sensors reading cycles
counter = 0;
counter2 ++;
if( !SETUP_MODE ) Idc = (((cumulIdc/measures) - Ioffset) *2500.0/convI/1023.0);
else Idc = (cumulIdc/measures);
if( Idc < 0 ) Idc = -Idc; // input DC current positive whenever the sensor connection
Vbat = (float)((cumulVbat/measures) *coefVbat*39.0/1023.0); // input voltage
Vht = (float)((cumulVht/measures) *coefVht*500.0/1023.0); // output HT voltage
Ths = (cumulThs/measures) *500/1023; // temperature in degrees Celsius
cumulVbat = 0;
cumulVht = 0;
cumulIdc = 0;
cumulThs = 0;
} // end of test counter
// what is done every time measures are completed
if( counter != 0 ) return; // everything after is done when counter = 0
// manage HTduty to get the 'VhtRef' output voltage
if( Vht < (VhtRef - deltaVht)) {
if( ++HTduty > HTdutyMax ) HTduty = HTdutyMax;
}
else if( Vht > (VhtRef + deltaVht)) {
if( --HTduty < 0 ) HTduty = 0;
}
// verify normal working conditions
if( Idc > IdcMax ) ACenable(3);
else if( Vbat < VbatMin ) ACenable(4);
else if( Vbat > VbatMax ) ACenable(5);
else if( Ths > ThsMax ) ACenable(6);
else if( Vht < (3*VhtRef/4)) ACenable(1);
else ACenable(0); // allow inverter pseudo sinus output
// set the PWM
OCR2A = HTduty; // set duty cycle
OCR2B = 255 - HTduty; // the inverting ratio
// LCD 1602 display management
if( counter2 > displayUpdate ) {
counter2 = 0;
lcd.setCursor(0, 0);
lcd.print("Vb: ");
if( Vbat < 10 ) lcd.print(" ");
lcd.print(Vbat);
lcd.setCursor(6, 0);
lcd.print("V Ib: ");
if( Idc < 10.0 ) lcd.print(Idc,1);
else lcd.print(Idc,0);
lcd.print("A");
if( !disable ) {
lcd.setCursor(0, 1);
lcd.print("Vs: ");
if( Vht < 100 ) lcd.print(" ");
lcd.print(Vht);
lcd.setCursor(7, 1);
lcd.print("V d: ");
int val = map( HTduty, 1, HTdutyMax, 0, 99 );
if( val < 10 ) lcd.print(" ");
lcd.print(val);
lcd.print("%");
}
}
// console monitoring
if( VERBOSE_MODE ) {
Serial.print(" Vbat= "); Serial.print(Vbat);
Serial.print("\t Vht= "); Serial.print(Vht);
Serial.print("\t Idc= "); Serial.print(Idc);
Serial.print("\t Ths= "); Serial.print(Ths);
Serial.print("\t HTduty= "); Serial.print(HTduty);
Serial.print("\t disable= "); Serial.print(disable);
Serial.println();
}
} // end of loop
//============================================================================================
// list of functions
//============================================================================================
//
// ACenable() : allow AC or display error message
//____________________________________________________________________________________________
void ACenable( byte reason ) {
if( reason == 0 ) {
if( disable ) {
for( byte i=0; i<3; i++ ) {
digitalWrite(ledPin, HIGH); delay(10);
digitalWrite(ledPin, LOW); delay(300);
}
digitalWrite(enablePin, HIGH); // enable AC
disable = false; // so that this is run once
} // end of test disable == true
} // end of test reason == 0
else if( reason == 1 ) {
digitalWrite(enablePin, LOW); // disable AC
disable = true;
lcd.setCursor(0, 1); lcd.print("! waiting for HT!");
}
else {
HTduty = 0; // stop the high volatge
digitalWrite(ledPin, HIGH); // alarm LED always on
disable = true;
switch( reason ) {
case 2: lcd.setCursor(0, 1); lcd.print("! short circuit!"); break;
case 3: lcd.setCursor(0, 1); lcd.print("! overcurrent !"); break;
case 4: lcd.setCursor(0, 1); lcd.print("! low battery !"); break;
case 5: lcd.setCursor(0, 1); lcd.print("! high battery !"); break;
case 6: lcd.setCursor(0, 1); lcd.print("! over heating !"); break;
default: lcd.setCursor(0, 1); lcd.print("! other error !"); break;
} // end of switch
} // end of else
} // end of EnableAC()
//
// ACgenerate() : function to create the 3 phase AC
//____________________________________________________________________________________________
void ACgenerate() {
static byte cycle = 0;
static bool trigger = false;
switch( cycle ) {
case 0: digitalWrite(ac1Pin, HIGH); digitalWrite(ac2Pin, LOW); digitalWrite(ac3Pin, HIGH); break;
case 1: digitalWrite(ac1Pin, HIGH); digitalWrite(ac2Pin, LOW); digitalWrite(ac3Pin, LOW); break;
case 2: digitalWrite(ac1Pin, HIGH); digitalWrite(ac2Pin, HIGH); digitalWrite(ac3Pin, LOW); break;
case 3: digitalWrite(ac1Pin, LOW); digitalWrite(ac2Pin, HIGH); digitalWrite(ac3Pin, LOW); break;
case 4: digitalWrite(ac1Pin, LOW); digitalWrite(ac2Pin, HIGH); digitalWrite(ac3Pin, HIGH); break;
case 5: digitalWrite(ac1Pin, LOW); digitalWrite(ac2Pin, LOW); digitalWrite(ac3Pin, HIGH); break;
} // end of switch
if( ++cycle >5 ) {
cycle = 0;
//digitalWrite(triggerPin, trigger);
//trigger = !trigger;
}
} // end of ACgenerate()
The article was first published in hackster, August 2 2020
cr: https://www.hackster.io/philippedc/arduino-dc-12-24v-to-ac-230v-380v-3-phases-inverter-afe5e1
author: philippedc