Doug Domke
Published © GPL3+

Arduino Based Pure Sine Wave Power Inverter

With this novel inverter design, an Arduino Nano replaces a lot of hardware, resulting in a simple pure sinewave inverter circuit

IntermediateFull instructions provided4 hours514
Arduino Based Pure Sine Wave Power Inverter

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
IRFB7437 Power MOSFET
×2
2N2222 NPN Transistor
×4
47K ohm resistor
×2
2.2K ohm resistor
×2
4.7K ohm resistor
×2
3.3K ohm resistor
×6
1 MFD 400 volt capacitor, non-polarized
×2
trim pot, 5K ohm
×1
10 mfd electrolytic capacitor
×1
Power Source - 12 volt battery or DC Power Supply
I actually used a 10 amp variable voltage bench power supply
×1
6-0-6 Transformer, 6 watt
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Pure Sinewave Inverter Schematic

Code

Pure Sinewave Inverter Code for Arduino Nano

Arduino
/*
 * This is a sketch for Arduino Nano to generate a pure sinewave output for an inverter.  It generates
 * PWM at 15360 Hz to produce a 60 Hz sinewave output.  It also senses feedback voltage and adjusts the
 * output to maintain 110 volt RMS output as the load and current output CHANGES. 
 */
 
// first quadrant of a sine wave, expressed as pulse widths in microseconds
const byte qtrSine[64] = {0, 1, 3, 4, 6, 7, 9, 10, 12, 14, 15, 17, 18, 
                          20, 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 36, 38, 39, 40, 41, 42,
                          44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 54, 55, 56, 57, 57, 58,
                          59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63};


// a scaled copy of sinewave - what is actually used by the timed interrupt routine 
volatile byte pulseWidth[64];
volatile int count = 0;
volatile int quadrant = 1;

float voltageError = 0;
float multiplier = 0.86; // initial value for 110v with no load

void setup() {
  // Serial.begin(9600);
  pinMode(8, OUTPUT);  // Port B0 - positive side output
  pinMode(12, OUTPUT);  // Port B4 - negativbe side output

  // set pulse widths for 110 v with no load
  resetPulseWidths(multiplier);

  // TIMER 1 for interrupt frequency 15360 Hz, i.e. 60 cycles * 256  (every 65 microseconds)
  cli(); // stop interrupts
  TCCR1A = 0; // set entire TCCR1A register to 0
  TCCR1B = 0; // same for TCCR1B
  TCNT1  = 0; // initialize counter value to 0
  // set compare match register for 15360 Hz increments
  OCR1A = 1040; // = 16000000 / (1 * 115360) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12, CS11 and CS10 bits for 1 prescaler
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei(); // allow interrupts
  delay(500);
}

void loop() {
  // main loop is a crude proportional controller
  // uses feedback voltage to adjust pulse widths of PWM to keep voltage 
  // around 110 volts as the load varies
  float feedbackVoltage = myVoltage();
  voltageError = (feedbackVoltage / 2.76)-1;
 
  /*  Serial.print(feedbackVoltage);
    Serial.print("  ");
    Serial.print(voltageError);
    Serial.print("  ");
    Serial.println(multiplier);
  */

 /* Feedback occurs here.  Gain of feedback is a combination of ratio of
 // voltage change to pulse width change (which is not linear) and a portion (1/4 here) of the voltage error
 // being applied to the pulse width change.  Not necessarily optimized, but at least gain is low 
 // enough to prevent oscillation.  Surprisingly, we can push the multiplier all the way to 2, significantly
 // distorting the input waveform without significant distortion of the outgoing sinewave.
 */
  if (voltageError  < - 0.02 || voltageError > 0.02) {
    multiplier = multiplier / (1 + voltageError/4);
    if (multiplier > 2) multiplier = 2;
    if (voltageError < - 0.9)  multiplier = 0.5;  // reduce output if load is excessive
    resetPulseWidths(multiplier);
  }
}

float myVoltage() { // return feedback voltage to Nano
  int myVolts = 0;
  for (int j = 0; j < 16; j++) { // 16 readings to filter out any ripple
    myVolts += analogRead(A0);
    delay(1);
  }
  return float(myVolts * 0.0012055); // ((adc_reads/16)*5)/1023 = voltage read by A0
}

// this routine scales the 60 cycle waveform up or down as dictated by the multiplier
// it does not allow the pulse width to exceed 63 microseconds, regardless of the multiplier.
void resetPulseWidths(float mult) {
  for (int i = 0; i < 64; i++) {
    pulseWidth[i] = qtrSine[i] * mult;
    if (pulseWidth[i] > 63) pulseWidth[i] = 63;
  }
}

ISR(TIMER1_COMPA_vect) {
  //interrupts occur 256 times for each 60 cycle wave
  count++;
  if (count > 63) {
    count = 0;
    quadrant++;
    if (quadrant > 4)quadrant = 1;
  }
  switch (quadrant) {
    case 1:
      PORTB |= (1 << PORTB0);// positive side output high
      delayMicroseconds(pulseWidth[count]);
      PORTB &= ~(1 << PORTB0); // positive side output low

      break;
    case 2:
      PORTB |= (1 << PORTB0);// positive side output high
      delayMicroseconds(pulseWidth[63 - count]);
      PORTB &= ~(1 << PORTB0); // positive side output low
      break;
    case 3:
      PORTB |= (1 << PORTB4);// negative side output high
      delayMicroseconds(pulseWidth[count]);
      PORTB &= ~(1 << PORTB4); // negative side output low
      break;
    case 4:
      PORTB |= (1 << PORTB4);// negative side output high
      delayMicroseconds(pulseWidth[63 - count]);
      PORTB &= ~(1 << PORTB4); // negative side output low
      break;
  }
}

Credits

Doug Domke

Doug Domke

25 projects • 98 followers

Comments