// include the library code:
#include <Arduino.h>
#include <LiquidCrystal.h>
#include "esp_adc_cal.h"
const float F_Ver = 1.02;
// Define constants for PWM channels and other parameters
const int PWM_FREQ = 5000; // PWM Carrier Frequency
const int PWM_RESOLUTION = 12; // PWM resolution in bits 12 Bits
const int NUM_POINTS = 256; // Number of points in the SPWM lookup table
//Setup PWM PINs
uint8_t PWM1H = 27;
uint8_t PWM1L = 4;
uint8_t PWM2H = 13;
uint8_t PWM2L = 12;
uint8_t BtnEnter = 18;
uint8_t BtnUp = 19;
uint8_t BtnDown = 21;
uint8_t PVAdc = 39;
// Desired SPWM frequency and actual set frequency
int SPWM_FREQ = 50; // Desired SPWM frequency in Hz
float V_ACOut = 0.0;
int Mppv = 250;
int SystemMode = 0;
int isFirst = 1;
// Lookup table for SPWM waveform
uint16_t spwm[NUM_POINTS];
// DC input voltage and desired AC output voltage
float V_DC = 300.0; // DC input voltage in Volts
const float V_AC = 230.0; // Desired AC output voltage in Volts RMS
int buttonState = 0;
hw_timer_t *timer = NULL;
//--Temp
int AN_Pot1_Result = 0;
int AN_Pot1_Result_Old = 2;
float PVVoltage = 0.0;
float calculateOutputVoltage() {
// Calculate RMS and peak AC voltage based on modulation index and DC input voltage
float m = V_AC / V_DC;
float rmsAC = m * V_DC / sqrt(2.0); // RMS value of AC voltage
float outputVoltage = rmsAC * sqrt(2.0); // Convert RMS to peak AC voltage
return outputVoltage;
}
// Initialize PWM channels
void initPWM() {
// Setup PWM channels Ver 2.0x
/* ledcSetup(0, PWM_FREQ, PWM_RESOLUTION);
ledcSetup(1, PWM_FREQ, PWM_RESOLUTION);
ledcSetup(2, PWM_FREQ, PWM_RESOLUTION);
ledcSetup(3, PWM_FREQ, PWM_RESOLUTION);
// Attach PWM channels to GPIO pins
ledcAttachPin(27, 0); // PWM channel 0, Q1
ledcAttachPin(4, 1); // PWM channel 1, Q2
ledcAttachPin(13, 2); // PWM channel 2, Q3
ledcAttachPin(12, 3); // PWM channel 3, Q4
*/
// Attach PWM channels to GPIO pins
//ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
ledcAttachChannel(PWM1H, PWM_FREQ, PWM_RESOLUTION,0); // PWM channel 0, Q1
ledcAttachChannel(PWM1L, PWM_FREQ, PWM_RESOLUTION,1); // PWM channel 1, Q2
ledcAttachChannel(PWM2H, PWM_FREQ, PWM_RESOLUTION,2); // PWM channel 2, Q3
ledcAttachChannel(PWM2L, PWM_FREQ, PWM_RESOLUTION,3); // PWM channel 3, Q4
}
// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(26, 25, 15, 14, 23, 22);
LiquidCrystal lcd(26, 25, 15, 14, 23, 22);
// Generate SPWM waveform in the lookup table
void generateSPWM() {
float phaseIncrement = 2 * PI / NUM_POINTS;
float maxSPWMValue = (pow(2, PWM_RESOLUTION) - 1) / 2;
for (int i = 0; i < NUM_POINTS; i++) {
float sinValue = sin(phaseIncrement * i);
// Scale sinValue to match desired output voltage (230V RMS)
spwm[i] = (uint16_t)((sinValue + 1) * (V_AC / V_DC) * maxSPWMValue);
}
for (int i = 0; i < NUM_POINTS; i++) {
Serial.print(spwm[i]);
Serial.print(" ");
}
Serial.println();
}
// Timer interrupt handler to update PWM duty cycles
void IRAM_ATTR onTimer() {
static int index = 0;
ledcWrite(PWM1H, spwm[index]); // Update Q1 duty cycle
ledcWrite(PWM1L, spwm[NUM_POINTS - 1 - index]); // Update Q2 duty cycle (complementary) NUM_POINTS - 1 - index
ledcWrite(PWM2H, spwm[index]); // Update Q3 duty cycle
ledcWrite(PWM2L, spwm[NUM_POINTS - 1 - index]); // Update Q4 duty cycle (complementary)
index = (index + 1) % NUM_POINTS; // Increment index and wrap around
}
void IRAM_ATTR btnDownPressed()
{
// buttonState = digitalRead(BtnDOWN);
if (SPWM_FREQ > 1 ) {
SPWM_FREQ = SPWM_FREQ - 1;
timerAlarm(timer, 1000000 / (NUM_POINTS * SPWM_FREQ), true, 0);
lcd.setCursor(0, 1);
lcd.print("Frequancy: ");
lcd.print(SPWM_FREQ);
lcd.print(" Hz ");
}
}
// Function to stop the timer interrupt
void stopTimerInterrupt() {
if (timer != NULL) {
timerEnd(timer); // Stop and detach the timer
timer = NULL;
}
}
void disablePWM(){
timerAlarm(timer, 0, false, 0);
ledcWrite(PWM1H, 0); // Update Q1 duty cycle
ledcWrite(PWM1L, 0); // Update Q2 duty cycle (complementary) NUM_POINTS - 1 - index
ledcWrite(PWM2H, 0); // Update Q3 duty cycle
ledcWrite(PWM2L, 0);
}
void enablePWM(){
timerAlarm(timer, 1000000 / (NUM_POINTS * SPWM_FREQ), true, 0);
}
// Function to initialize the timer interrupt Ver 2.0x
/*void initTimerInterruptx() {
timer = timerBegin(0, 80, true); // timer 0, prescaler 80, count up
timerAttachInterrupt(timer, &onTimer, true); // Attach interrupt on timer
timerAlarmWrite(timer, 1000000 / (NUM_POINTS * SPWM_FREQ), true); // Set interrupt period
timerAlarmEnable(timer); // Enable the interrupt
Serial.println("Timer Reset");
}*/
void initTimerInterrupt() {
timer = timerBegin(1000000); // timer 0, prescaler 80, count up
timerAttachInterrupt(timer, &onTimer); // Attach interrupt on timer
//timerAlarm(timer, 1000000 / (NUM_POINTS * SPWM_FREQ), true, 0);
Serial.println("Timer Reset");
}
uint32_t readADC_Cal(int ADC_Raw)
{
esp_adc_cal_characteristics_t adc_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
return(esp_adc_cal_raw_to_voltage(ADC_Raw, &adc_chars));
}
// Initialize SPWM and PWM channels
void setup() {
// Initialize serial communication for debugging
Serial.begin(115200);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.print("IAT PV Invertor");// Print a message to the LCD.
lcd.setCursor(0, 1);
lcd.print(" Ver: ");
lcd.print(F_Ver);
// Initialize PWM channels
initPWM();
generateSPWM();
Serial.println("SPWM waveform generated and PWM channels initialized.");
initTimerInterrupt();
// Generate SPWM waveform
// Setup timer interrupt for PWM synchronization
Serial.println("PWM signals synchronized.");
// Delete After Testing
Serial.println("0 SPWM waveform values:");
pinMode(BtnEnter, INPUT);
pinMode(BtnEnter, INPUT_PULLUP);
pinMode(BtnDown, INPUT);
pinMode(BtnDown, INPUT_PULLUP);
pinMode(BtnUp, INPUT);
pinMode(BtnUp, INPUT_PULLUP);
// Button Interrupts
attachInterrupt(digitalPinToInterrupt(BtnDown), btnDownPressed, RISING);
delay(2000);
}
int btnPressed = 1;
// Main loop (not used in this example)
void loop() {
// ADC Section
AN_Pot1_Result = analogRead(PVAdc);
PVVoltage =(AN_Pot1_Result*3.3)/4095; // ADC Values 12 bit 0-4095(3.3V), conver;to Volts
if (AN_Pot1_Result_Old != AN_Pot1_Result){
AN_Pot1_Result_Old = AN_Pot1_Result;
PVVoltage = int(round((PVVoltage+0.8)*100));
Serial.print("ADC"); Serial.println(PVVoltage);
Serial.print("V_DC"); Serial.println(V_DC);
Serial.print("Diff: "); Serial.println(abs(PVVoltage- V_DC));
if (PVVoltage > Mppv & SystemMode == 0){//enable PWM if it was previously disabled due to Low Volt error
V_DC = PVVoltage;
generateSPWM();
enablePWM();
SystemMode = 1;
}
if (PVVoltage < Mppv) { //Under Volt, stop SPWM
disablePWM();
SystemMode = 0;
}
if (abs(PVVoltage- V_DC)>10){ // if PV Volt ae changed adjest Lookup points for stable output
if (PVVoltage>= 300 and PVVoltage<=410){
V_DC = PVVoltage;
generateSPWM();
Serial.print("New PWM Generated With New V_DC"); Serial.println(V_DC);
}
}
if (SystemMode == 0) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Low PV Error ");
}else{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("PvV: ");
lcd.print((int)PVVoltage);
lcd.setCursor(0, 1);
lcd.print("Frequancy: ");
lcd.print(SPWM_FREQ);
lcd.print(" Hz");
}
V_ACOut =calculateOutputVoltage();
/*Serial.print("Real-time Output Voltage: ");
Serial.print(SPWM_FREQ);
Serial.println(" H");
Serial.print("Current SPWM Frequancy: ");
Serial.print(V_ACOut);
Serial.println(" V");
*/
buttonState = digitalRead(BtnUp);
if (buttonState == HIGH && btnPressed != buttonState) {
Serial.println(buttonState);
btnPressed = buttonState;
} else if (buttonState == LOW && btnPressed != buttonState) {
Serial.println(buttonState);
btnPressed = buttonState;
SPWM_FREQ = SPWM_FREQ + 1;
// timerAlarmWrite(timer, 1000000 / (NUM_POINTS * SPWM_FREQ * 2), true);
timerAlarm(timer, 1000000 / (NUM_POINTS * SPWM_FREQ), true, 0);
lcd.setCursor(0, 1);
lcd.print("Frequancy: ");
lcd.print(SPWM_FREQ);
lcd.print(" Hz");
}
//V_DC = int(round(Voltage+0.8)*100));
if (SystemMode ==1) {
lcd.setCursor(0, 0);
lcd.print("PvV: ");
lcd.setCursor(5, 0);
lcd.print( (int)PVVoltage);
}
}
delay(1000);
// Nothing in the loop for this example
}