for INA226
parent
472a299506
commit
a743759fd7
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
void ADC_SetGain(){
|
||||||
|
if(ADS1015_Mode==true){ //FOR ADS1015 12-BIT ADC MODEL
|
||||||
|
if(ADC_GainSelect==0){ads.setGain(GAIN_TWOTHIRDS);ADC_BitReso=3.0000;} // Gain: 2/3x Range: +/- 6.144V
|
||||||
|
else if(ADC_GainSelect==1){ads.setGain(GAIN_ONE);ADC_BitReso=2.0000;} // Gain: 1x Range: +/- 4.096V
|
||||||
|
else if(ADC_GainSelect==2){ads.setGain(GAIN_TWO);ADC_BitReso=1.0000;} // Gain: 2x Range: +/- 2.048V
|
||||||
|
}
|
||||||
|
else{ //FOR ADS1115 16-BIT ADC MODEL
|
||||||
|
if(ADC_GainSelect==0){ads.setGain(GAIN_TWOTHIRDS);ADC_BitReso= 0.1875;} // Gain: 2/3x Range: +/- 6.144V
|
||||||
|
else if(ADC_GainSelect==1){ads.setGain(GAIN_ONE);ADC_BitReso= 0.125;} // Gain: 1x Range: +/- 4.096V
|
||||||
|
else if(ADC_GainSelect==2){ads.setGain(GAIN_TWO);ADC_BitReso= 0.0625;} // Gain: 2x Range: +/- 2.048V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void checkConfig(INA226 &ina)
|
||||||
|
{
|
||||||
|
Serial.print("Mode: ");
|
||||||
|
switch (ina.getMode())
|
||||||
|
{
|
||||||
|
case INA226_MODE_POWER_DOWN: Serial.println("Power-Down"); break;
|
||||||
|
case INA226_MODE_SHUNT_TRIG: Serial.println("Shunt Voltage, Triggered"); break;
|
||||||
|
case INA226_MODE_BUS_TRIG: Serial.println("Bus Voltage, Triggered"); break;
|
||||||
|
case INA226_MODE_SHUNT_BUS_TRIG: Serial.println("Shunt and Bus, Triggered"); break;
|
||||||
|
case INA226_MODE_ADC_OFF: Serial.println("ADC Off"); break;
|
||||||
|
case INA226_MODE_SHUNT_CONT: Serial.println("Shunt Voltage, Continuous"); break;
|
||||||
|
case INA226_MODE_BUS_CONT: Serial.println("Bus Voltage, Continuous"); break;
|
||||||
|
case INA226_MODE_SHUNT_BUS_CONT: Serial.println("Shunt and Bus, Continuous"); break;
|
||||||
|
default: Serial.println("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Samples average: ");
|
||||||
|
switch (ina.getAverages())
|
||||||
|
{
|
||||||
|
case INA226_AVERAGES_1: Serial.println("1 sample"); break;
|
||||||
|
case INA226_AVERAGES_4: Serial.println("4 samples"); break;
|
||||||
|
case INA226_AVERAGES_16: Serial.println("16 samples"); break;
|
||||||
|
case INA226_AVERAGES_64: Serial.println("64 samples"); break;
|
||||||
|
case INA226_AVERAGES_128: Serial.println("128 samples"); break;
|
||||||
|
case INA226_AVERAGES_256: Serial.println("256 samples"); break;
|
||||||
|
case INA226_AVERAGES_512: Serial.println("512 samples"); break;
|
||||||
|
case INA226_AVERAGES_1024: Serial.println("1024 samples"); break;
|
||||||
|
default: Serial.println("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Bus conversion time: ");
|
||||||
|
switch (ina.getBusConversionTime())
|
||||||
|
{
|
||||||
|
case INA226_BUS_CONV_TIME_140US: Serial.println("140uS"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_204US: Serial.println("204uS"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_332US: Serial.println("332uS"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_588US: Serial.println("558uS"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_1100US: Serial.println("1.100ms"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_2116US: Serial.println("2.116ms"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_4156US: Serial.println("4.156ms"); break;
|
||||||
|
case INA226_BUS_CONV_TIME_8244US: Serial.println("8.244ms"); break;
|
||||||
|
default: Serial.println("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Shunt conversion time: ");
|
||||||
|
switch (ina.getShuntConversionTime())
|
||||||
|
{
|
||||||
|
case INA226_SHUNT_CONV_TIME_140US: Serial.println("140uS"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_204US: Serial.println("204uS"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_332US: Serial.println("332uS"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_588US: Serial.println("558uS"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_1100US: Serial.println("1.100ms"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_2116US: Serial.println("2.116ms"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_4156US: Serial.println("4.156ms"); break;
|
||||||
|
case INA226_SHUNT_CONV_TIME_8244US: Serial.println("8.244ms"); break;
|
||||||
|
default: Serial.println("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("Max possible current: ");
|
||||||
|
Serial.print(ina.getMaxPossibleCurrent());
|
||||||
|
Serial.println(" A");
|
||||||
|
|
||||||
|
Serial.print("Max current: ");
|
||||||
|
Serial.print(ina.getMaxCurrent());
|
||||||
|
Serial.println(" A");
|
||||||
|
|
||||||
|
Serial.print("Max shunt voltage: ");
|
||||||
|
Serial.print(ina.getMaxShuntVoltage());
|
||||||
|
Serial.println(" V");
|
||||||
|
|
||||||
|
Serial.print("Max power: ");
|
||||||
|
Serial.print(ina.getMaxPower());
|
||||||
|
Serial.println(" W");
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetVariables() {
|
||||||
|
secondsElapsed = 0;
|
||||||
|
energySavings = 0;
|
||||||
|
daysRunning = 0;
|
||||||
|
timeOn = 0;
|
||||||
|
}
|
||||||
|
void Read_Sensors() {
|
||||||
|
|
||||||
|
/////////// TEMPERATURE SENSOR /////////////
|
||||||
|
if (sampleStoreTS <= avgCountTS) { //TEMPERATURE SENSOR - Lite Averaging
|
||||||
|
TS = TS + analogRead(TempSensor);
|
||||||
|
sampleStoreTS++;
|
||||||
|
} else {
|
||||||
|
TS = TS / sampleStoreTS;
|
||||||
|
TSlog = log(ntcResistance * (4095.00 / TS - 1.00));
|
||||||
|
temperature = (1.0 / (1.009249522e-03 + 2.378405444e-04 * TSlog + 2.019202697e-07 * TSlog * TSlog * TSlog)) - 273.15+10;
|
||||||
|
sampleStoreTS = 0;
|
||||||
|
TS = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////// VOLTAGE & CURRENT SENSORS /////////////
|
||||||
|
VSI = 0.0000; //Clear Previous Input Voltage
|
||||||
|
VSO = 0.0000; //Clear Previous Output Voltage
|
||||||
|
CSI = 0.0000; //Clear Previous Input Current
|
||||||
|
CSO = 0.0000; //Clear Previous Output Current
|
||||||
|
|
||||||
|
//VOLTAGE SENSOR - Instantenous Averaging
|
||||||
|
/*
|
||||||
|
for (int i = 0; i < avgCountVS; i++) {
|
||||||
|
VSI = VSI + ads.computeVolts(ads.readADC_SingleEnded(3));
|
||||||
|
VSO = VSO + ads.computeVolts(ads.readADC_SingleEnded(1));
|
||||||
|
VSI = VSI + ina1.readBusVoltage()*(R1_VOLTAGE1+R2_VOLTAGE1)/R1_VOLTAGE1;
|
||||||
|
VSO = VSO + ina2.readBusVoltage()*(R1_VOLTAGE2+R2_VOLTAGE2)/R1_VOLTAGE2;
|
||||||
|
}
|
||||||
|
voltageInput = (VSI/avgCountVS)*inVoltageDivRatio;
|
||||||
|
voltageOutput = (VSO/avgCountVS)*outVoltageDivRatio;
|
||||||
|
voltageInput = VSI/avgCountVS;
|
||||||
|
voltageOutput = VSO/avgCountVS;
|
||||||
|
*/
|
||||||
|
voltageInput = ina1.readBusVoltage() * inVoltageDivRatio;
|
||||||
|
voltageOutput = ina2.readBusVoltage() * outVoltageDivRatio;
|
||||||
|
|
||||||
|
|
||||||
|
//CURRENT SENSOR - Instantenous Averaging
|
||||||
|
/*
|
||||||
|
for (int i = 0; i < avgCountCS; i++) {
|
||||||
|
CSI = CSI + ads.computeVolts(ads.readADC_SingleEnded(2));
|
||||||
|
CSI = CSI + ina1.readShuntCurrent();
|
||||||
|
CSO = CSO + ina2.readShuntCurrent();
|
||||||
|
}
|
||||||
|
CSI_converted = (CSI/avgCountCS)*1.3300;
|
||||||
|
currentInput = ((CSI_converted-currentMidPoint)*-1)/currentSensV;
|
||||||
|
CSI_converted = (CSI/avgCountCS);
|
||||||
|
CSO_converted = (CSO/avgCountCS);
|
||||||
|
*/
|
||||||
|
CSI_converted = ina1.readShuntCurrent();
|
||||||
|
CSO_converted = ina2.readShuntCurrent();
|
||||||
|
currentInput = CSI_converted;
|
||||||
|
|
||||||
|
if (currentInput < 0) {
|
||||||
|
currentInput = 0.0000;
|
||||||
|
}
|
||||||
|
if (voltageOutput <= 0) {
|
||||||
|
currentOutput = 0.0000;
|
||||||
|
}
|
||||||
|
//else{currentOutput = (voltageInput*currentInput)/voltageOutput;}
|
||||||
|
else {
|
||||||
|
currentOutput = CSO_converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
//POWER SOURCE DETECTION
|
||||||
|
if (voltageInput <= 3 && voltageOutput <= 3) {
|
||||||
|
inputSource = 0; //System is only powered by USB port
|
||||||
|
}
|
||||||
|
else if (voltageInput > voltageOutput) {
|
||||||
|
inputSource = 1; //System is running on solar as power source
|
||||||
|
}
|
||||||
|
else if (voltageInput < voltageOutput) {
|
||||||
|
inputSource = 2; //System is running on batteries as power source
|
||||||
|
}
|
||||||
|
|
||||||
|
//////// AUTOMATIC CURRENT SENSOR CALIBRATION ////////
|
||||||
|
if (buckEnable == 0 && FLV == 0 && OOV == 0) {
|
||||||
|
//currentMidPoint = ((CSI/avgCountCS)*1.3300)-0.003;
|
||||||
|
currentMidPoint = (CSI / avgCountCS) - 0.003;
|
||||||
|
}
|
||||||
|
|
||||||
|
//POWER COMPUTATION - Through computation
|
||||||
|
//powerInput = voltageInput*currentInput;
|
||||||
|
powerInput = ina1.readBusPower();
|
||||||
|
//powerOutput = voltageInput*currentInput*efficiencyRate;
|
||||||
|
powerOutput = ina2.readBusPower();
|
||||||
|
outputDeviation = (voltageOutput / voltageBatteryMax) * 100.000;
|
||||||
|
|
||||||
|
//STATE OF CHARGE - Battery Percentage
|
||||||
|
batteryPercent = ((voltageOutput - voltageBatteryMin) / (voltageBatteryMax - voltageBatteryMin)) * 101;
|
||||||
|
batteryPercent = constrain(batteryPercent, 0, 100);
|
||||||
|
|
||||||
|
//TIME DEPENDENT SENSOR DATA COMPUTATION
|
||||||
|
currentRoutineMillis = millis();
|
||||||
|
if (currentRoutineMillis - prevRoutineMillis >= millisRoutineInterval) { //Run routine every millisRoutineInterval (ms)
|
||||||
|
prevRoutineMillis = currentRoutineMillis; //Store previous time
|
||||||
|
Wh = Wh + (powerInput / (3600.000 * (1000.000 / millisRoutineInterval))); //Accumulate and compute energy harvested (3600s*(1000/interval))
|
||||||
|
kWh = Wh / 1000.000;
|
||||||
|
MWh = Wh / 1000000.000;
|
||||||
|
daysRunning = timeOn / (86400.000 * (1000.000 / millisRoutineInterval)); //Compute for days running (86400s*(1000/interval))
|
||||||
|
timeOn++; //Increment time counter
|
||||||
|
}
|
||||||
|
|
||||||
|
//OTHER DATA
|
||||||
|
secondsElapsed = millis() / 1000; //Gets the time in seconds since the was turned on and active
|
||||||
|
energySavings = electricalPrice * (Wh / 1000.0000); //Computes the solar energy saving in terms of money (electricity flag rate)
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
void backflowControl() { //PV BACKFLOW CONTROL (INPUT MOSFET)
|
||||||
|
if (output_Mode == 0) {
|
||||||
|
bypassEnable = 1; //PSU MODE: Force backflow MOSFET on
|
||||||
|
}
|
||||||
|
else { //CHARGER MODE: Force backflow MOSFET on
|
||||||
|
if (voltageInput > voltageOutput + voltageDropout) {
|
||||||
|
bypassEnable = 1; //CHARGER MODE: Normal Condition - Turn on Backflow MOSFET (on by default when not in MPPT charger mode)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bypassEnable = 0; //CHARGER MODE: Input Undervoltage - Turn off bypass MOSFET and prevent PV Backflow (SS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
digitalWrite(backflow_MOSFET, bypassEnable); //Signal backflow MOSFET GPIO pin
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device_Protection() {
|
||||||
|
//ERROR COUNTER RESET
|
||||||
|
currentRoutineMillis = millis();
|
||||||
|
if (currentErrorMillis - prevErrorMillis >= errorTimeLimit) { //Run routine every millisErrorInterval (ms)
|
||||||
|
prevErrorMillis = currentErrorMillis; //Store previous time
|
||||||
|
if (errorCount < errorCountLimit) {
|
||||||
|
errorCount = 0; //Reset error count if it is below the limit before x milliseconds
|
||||||
|
}
|
||||||
|
else {} // TO ADD: sleep and charging pause if too many errors persists
|
||||||
|
}
|
||||||
|
//FAULT DETECTION
|
||||||
|
ERR = 0; //Reset local error counter
|
||||||
|
backflowControl(); //Run backflow current protection protocol
|
||||||
|
if (temperature > temperatureMax) {
|
||||||
|
OTE = 1; //OTE - OVERTEMPERATURE: System overheat detected
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
OTE = 0;
|
||||||
|
}
|
||||||
|
if (currentInput > currentInAbsolute) {
|
||||||
|
IOC = 1; //IOC - INPUT OVERCURRENT: Input current has reached absolute limit
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
IOC = 0;
|
||||||
|
}
|
||||||
|
if (currentOutput > currentOutAbsolute) {
|
||||||
|
OOC = 1; //OOC - OUTPUT OVERCURRENT: Output current has reached absolute limit
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
OOC = 0;
|
||||||
|
}
|
||||||
|
if (voltageOutput > voltageBatteryMax + voltageBatteryThresh) {
|
||||||
|
OOV = 1; //OOV - OUTPUT OVERVOLTAGE: Output voltage has reached absolute limit
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
OOV = 0;
|
||||||
|
}
|
||||||
|
if (voltageInput < vInSystemMin && voltageOutput < vInSystemMin) {
|
||||||
|
FLV = 1; //FLV - Fatally low system voltage (unable to resume operation)
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
FLV = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_Mode == 0) { //PSU MODE specific protection protocol
|
||||||
|
REC = 0; BNC = 0; //Clear recovery and battery not connected boolean identifiers
|
||||||
|
if (voltageInput < voltageBatteryMax + voltageDropout) {
|
||||||
|
IUV = 1; //IUV - INPUT UNDERVOLTAGE: Input voltage is below battery voltage (for psu mode only)
|
||||||
|
ERR++;
|
||||||
|
errorCount++;
|
||||||
|
} else {
|
||||||
|
IUV = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { //Charger MODE specific protection protocol
|
||||||
|
backflowControl(); //Enable backflow current detection & control
|
||||||
|
if(voltageOutput<vInSystemMin) {BNC=1;ERR++;} else{BNC=0;} //BNC - BATTERY NOT CONNECTED (for charger mode only, does not treat BNC as error when not under MPPT mode)
|
||||||
|
if (voltageInput < voltageBatteryMax + voltageDropout) {
|
||||||
|
IUV = 1; //IUV - INPUT UNDERVOLTAGE: Input voltage is below max battery charging voltage (for charger mode only)
|
||||||
|
ERR++;
|
||||||
|
REC = 1;
|
||||||
|
} else {
|
||||||
|
IUV = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
void buck_Enable(){ //启用 MPPT 降压转换器
|
||||||
|
buckEnable = 1;
|
||||||
|
digitalWrite(buck_EN,HIGH);
|
||||||
|
digitalWrite(LED,HIGH);
|
||||||
|
}
|
||||||
|
void buck_Disable(){ //禁用 MPPT 降压转换器
|
||||||
|
buckEnable = 0;
|
||||||
|
digitalWrite(buck_EN,LOW);
|
||||||
|
digitalWrite(LED,LOW);
|
||||||
|
PWM = 0;
|
||||||
|
}
|
||||||
|
void predictivePWM(){ //预测PWM 算法
|
||||||
|
if(voltageInput<=0){PPWM=0;} ///当电压输入为零时防止不确定答案
|
||||||
|
else if(voltageOutput>voltageBatteryMax){PPWM =(PPWM_margin*pwmMax*voltageBatteryMax)/(100.00*voltageInput)*0.98;} //计算预测 PWM Floor 并存储在变量中
|
||||||
|
else{PPWM =(PPWM_margin*pwmMax*voltageOutput)/(100.00*voltageInput);} //计算预测 PWM Floor 并存储在变量中
|
||||||
|
PPWM = constrain(PPWM,0,pwmMaxLimited);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PWM_Modulation(){
|
||||||
|
if(output_Mode==0){PWM = constrain(PWM,0,pwmMaxLimited);} //PSU MODE PWM = PWM OVERFLOW PROTECTION(将下限限制为 0%,上限限制为最大允许占空比)
|
||||||
|
else{
|
||||||
|
predictivePWM(); //运行和计算预测 pwm floor
|
||||||
|
PWM = constrain(PWM,PPWM,pwmMaxLimited); //CHARGER MODE PWM - 将下限限制为 PPWM,上限限制为最大允许占空比)
|
||||||
|
}
|
||||||
|
ledcWrite(pwmChannel,PWM); //设置 PWM 占空比并在启用降压时写入 GPIO
|
||||||
|
buck_Enable(); //开启 MPPT 降压 (IR2104)
|
||||||
|
}
|
||||||
|
|
||||||
|
void Charging_Algorithm(){
|
||||||
|
if(ERR>0||chargingPause==1){buck_Disable();} //当出现错误或充电暂停用于暂停覆盖时关闭 MPPT 降压
|
||||||
|
else{
|
||||||
|
if(REC==1){ // IUV RECOVERY - (仅对充电模式有效)
|
||||||
|
REC=0; //重置 IUV 恢复布尔标识符
|
||||||
|
buck_Disable(); //在 PPWM 初始化之前禁用降压
|
||||||
|
lcd.setCursor(0,0);lcd.print("POWER SOURCE "); //显示液晶信息
|
||||||
|
lcd.setCursor(0,1);lcd.print("DETECTED "); //显示液晶信息
|
||||||
|
Serial.println("> Solar Panel Detected"); //显示串口信息
|
||||||
|
Serial.print("> Computing For Predictive PWM "); //显示串口信息
|
||||||
|
for(int i = 0; i<40; i++){Serial.print(".");delay(30);} //For loop "loading... 效果
|
||||||
|
Serial.println(""); //在串行上显示下一行的换行符
|
||||||
|
Read_Sensors();
|
||||||
|
predictivePWM();
|
||||||
|
PWM = PPWM;
|
||||||
|
lcd.clear();
|
||||||
|
}
|
||||||
|
else{ //NO ERROR PRESENT - 继续电源转换
|
||||||
|
/////////////////////// CC-CV BUCK PSU ALGORITHM //////////////////////////////
|
||||||
|
if(MPPT_Mode==0){ // CC-CV PSU 模式
|
||||||
|
if(currentOutput>currentCharging) {PWM--;} //电流高于 → 降低占空比
|
||||||
|
else if(voltageOutput>voltageBatteryMax){PWM--;} //电压高于 → 降低占空比
|
||||||
|
else if(voltageOutput<voltageBatteryMax){PWM++;} //当输出低于充电电压时增加占空比(仅用于 CC-CV 模式)
|
||||||
|
else{} //当达到设定的输出电压时什么都不做
|
||||||
|
PWM_Modulation(); //将 PWM 信号设置为 Buck PWM GPIO
|
||||||
|
}
|
||||||
|
/////////////////////// MPPT & CC-CV 充电算法 ///////////////////////
|
||||||
|
else{
|
||||||
|
if(currentOutput>currentCharging){PWM--;} //电流高于 → 降低占空比
|
||||||
|
else if(voltageOutput>voltageBatteryMax){PWM--;} //电压高于 → 降低占空比
|
||||||
|
else{ //MPPT 算法
|
||||||
|
if(powerInput>powerInputPrev && voltageInput>voltageInputPrev) {PWM--;} // ↑P ↑V ; →MPP //D--
|
||||||
|
else if(powerInput>powerInputPrev && voltageInput<voltageInputPrev){PWM++;} // ↑P ↓V ; MPP← //D++
|
||||||
|
else if(powerInput<powerInputPrev && voltageInput>voltageInputPrev){PWM++;} // ↓P ↑V ; MPP→ //D++
|
||||||
|
else if(powerInput<powerInputPrev && voltageInput<voltageInputPrev){PWM--;} // ↓P ↓V ; ←MPP //D--
|
||||||
|
else if(voltageOutput>voltageBatteryMax) {PWM--;} // MP MV ; 达到 MPP
|
||||||
|
else if(voltageOutput<voltageBatteryMax) {PWM++;} // MP MV ; 达到 MPP
|
||||||
|
powerInputPrev = powerInput; // 存储以前记录的功率
|
||||||
|
voltageInputPrev = voltageInput; //存储先前记录的电压
|
||||||
|
}
|
||||||
|
PWM_Modulation(); //将 PWM 信号设置为 Buck PWM GPIO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
void System_Processes() {
|
||||||
|
///////////////// FAN COOLING /////////////////
|
||||||
|
if (enableFan == true) {
|
||||||
|
if (enableDynamicCooling == false) { //STATIC PWM COOLING MODE (2-PIN FAN - no need for hysteresis, temp data only refreshes after 'avgCountTS' or every 500 loop cycles)
|
||||||
|
if (overrideFan == true) {
|
||||||
|
fanStatus = true; //Force on fan
|
||||||
|
}
|
||||||
|
else if (temperature >= temperatureFan) {
|
||||||
|
fanStatus = 1; //Turn on fan when set fan temp reached
|
||||||
|
}
|
||||||
|
else if (temperature < temperatureFan) {
|
||||||
|
fanStatus = 0; //Turn off fan when set fan temp reached
|
||||||
|
}
|
||||||
|
digitalWrite(FAN, fanStatus); //Send a digital signal to the fan MOSFET
|
||||||
|
}
|
||||||
|
else {} //DYNAMIC PWM COOLING MODE (3-PIN FAN - coming soon)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
digitalWrite(FAN, LOW); //Fan Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////// LOOP TIME STOPWATCH ////////////
|
||||||
|
loopTimeStart = micros(); //Record Start Time
|
||||||
|
loopTime = (loopTimeStart - loopTimeEnd) / 1000.000; //Compute Loop Cycle Speed (mS)
|
||||||
|
loopTimeEnd = micros(); //Record End Time
|
||||||
|
|
||||||
|
///////////// AUTO DATA RESET /////////////
|
||||||
|
if (telemCounterReset == 0) {} //Never Reset
|
||||||
|
else if (telemCounterReset == 1 && daysRunning > 1) {
|
||||||
|
resetVariables(); //Daily Reset
|
||||||
|
}
|
||||||
|
else if (telemCounterReset == 2 && daysRunning > 7) {
|
||||||
|
resetVariables(); //Weekly Reset
|
||||||
|
}
|
||||||
|
else if (telemCounterReset == 3 && daysRunning > 30) {
|
||||||
|
resetVariables(); //Monthly Reset
|
||||||
|
}
|
||||||
|
else if (telemCounterReset == 4 && daysRunning > 365) {
|
||||||
|
resetVariables(); //Yearly Reset
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////// LOW POWER MODE /////////////
|
||||||
|
if (lowPowerMode == 1) {}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void factoryReset() {
|
||||||
|
EEPROM.begin(eeprom_size);
|
||||||
|
EEPROM.write(MPPT_Mode_add, 1); //STORE: Charging Algorithm (1 = MPPT Mode)
|
||||||
|
EEPROM.write(voltageBatteryMax_add, 12); //STORE: Max Battery Voltage (whole)
|
||||||
|
EEPROM.write(voltageBatteryMax2_add, 0); //STORE: Max Battery Voltage (decimal)
|
||||||
|
EEPROM.write(voltageBatteryMin_add, 9); //STORE: Min Battery Voltage (whole)
|
||||||
|
EEPROM.write(voltageBatteryMin2_add, 0); //STORE: Min Battery Voltage (decimal)
|
||||||
|
EEPROM.write(currentCharging_add, 1); //STORE: Charging Current (whole)
|
||||||
|
EEPROM.write(currentCharging2_add, 0); //STORE: Charging Current (decimal)
|
||||||
|
EEPROM.write(enableFan_add, 1); //STORE: Fan Enable (Bool)
|
||||||
|
EEPROM.write(temperatureFan_add, 60); //STORE: Fan Temp (Integer)
|
||||||
|
EEPROM.write(temperatureMax_add, 90); //STORE: Shutdown Temp (Integer)
|
||||||
|
EEPROM.write(enableWiFi_add, 1); //STORE: Enable WiFi (Boolean)
|
||||||
|
EEPROM.write(flashMemLoad_add, 1); //STORE: Enable autoload (on by default)
|
||||||
|
EEPROM.write(output_Mode_add, 1); //STORE: Charger/PSU Mode Selection (1 = Charger Mode)
|
||||||
|
EEPROM.write(backlightSleepMode_add, 0); //STORE: LCD backlight sleep timer (default: 0 = never)
|
||||||
|
EEPROM.commit();
|
||||||
|
loadSettings();
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadSettings() {
|
||||||
|
MPPT_Mode = EEPROM.read(MPPT_Mode_add); // Load saved charging mode setting
|
||||||
|
voltageBatteryMax = EEPROM.read(voltageBatteryMax_add) + (EEPROM.read(voltageBatteryMax2_add) * .01); // Load saved maximum battery voltage setting
|
||||||
|
voltageBatteryMin = EEPROM.read(voltageBatteryMin_add) + (EEPROM.read(voltageBatteryMin2_add) * .01); // Load saved minimum battery voltage setting
|
||||||
|
currentCharging = EEPROM.read(currentCharging_add) + (EEPROM.read(currentCharging2_add) * .01); // Load saved charging current setting
|
||||||
|
enableFan = EEPROM.read(enableFan_add); // Load saved fan enable settings
|
||||||
|
temperatureFan = EEPROM.read(temperatureFan_add); // Load saved fan temperature settings
|
||||||
|
temperatureMax = EEPROM.read(temperatureMax_add); // Load saved shutdown temperature settings
|
||||||
|
enableWiFi = EEPROM.read(enableWiFi_add); // Load saved WiFi enable settings
|
||||||
|
flashMemLoad = EEPROM.read(flashMemLoad_add); // Load saved flash memory autoload feature
|
||||||
|
output_Mode = EEPROM.read(output_Mode_add); // Load saved charging mode setting
|
||||||
|
backlightSleepMode = EEPROM.read(backlightSleepMode_add); // Load saved lcd backlight sleep timer
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveSettings() {
|
||||||
|
EEPROM.begin(eeprom_size);
|
||||||
|
EEPROM.write(MPPT_Mode_add, MPPT_Mode); //STORE: Algorithm
|
||||||
|
conv1 = voltageBatteryMax * 100; //STORE: Maximum Battery Voltage (gets whole number)
|
||||||
|
conv2 = conv1 % 100; //STORE: Maximum Battery Voltage (gets decimal number and converts to a whole number)
|
||||||
|
EEPROM.write(voltageBatteryMax_add, voltageBatteryMax);
|
||||||
|
EEPROM.write(voltageBatteryMax2_add, conv2);
|
||||||
|
conv1 = voltageBatteryMin * 100; //STORE: Minimum Battery Voltage (gets whole number)
|
||||||
|
conv2 = conv1 % 100; //STORE: Minimum Battery Voltage (gets decimal number and converts to a whole number)
|
||||||
|
EEPROM.write(voltageBatteryMin_add, voltageBatteryMin);
|
||||||
|
EEPROM.write(voltageBatteryMin2_add, conv2);
|
||||||
|
conv1 = currentCharging * 100; //STORE: Charging Current
|
||||||
|
conv2 = conv1 % 100;
|
||||||
|
EEPROM.write(currentCharging_add, currentCharging);
|
||||||
|
EEPROM.write(currentCharging2_add, conv2);
|
||||||
|
EEPROM.write(enableFan_add, enableFan); //STORE: Fan Enable
|
||||||
|
EEPROM.write(temperatureFan_add, temperatureFan); //STORE: Fan Temp
|
||||||
|
EEPROM.write(temperatureMax_add, temperatureMax); //STORE: Shutdown Temp
|
||||||
|
EEPROM.write(enableWiFi_add, enableWiFi); //STORE: Enable WiFi
|
||||||
|
//EEPROM.write(flashMemLoad_add, flashMemLoad); //STORE: Enable autoload (must be excluded from bulk save, uncomment under discretion)
|
||||||
|
EEPROM.write(output_Mode_add, output_Mode); //STORE: Charge/PSU Mode Selection
|
||||||
|
EEPROM.write(backlightSleepMode_add, backlightSleepMode); //STORE: LCD backlight sleep timer
|
||||||
|
EEPROM.commit(); //Saves setting changes to flash memory
|
||||||
|
EEPROM.end();
|
||||||
|
|
||||||
|
}
|
||||||
|
void saveAutoloadSettings() {
|
||||||
|
EEPROM.begin(eeprom_size);
|
||||||
|
EEPROM.write(flashMemLoad_add, flashMemLoad); //STORE: Enable autoload
|
||||||
|
EEPROM.commit(); //Saves setting changes to flash memory
|
||||||
|
EEPROM.end();
|
||||||
|
Serial.println("Saves setting changes");
|
||||||
|
}
|
||||||
|
void initializeFlashAutoload() {
|
||||||
|
if (disableFlashAutoLoad == 0) {
|
||||||
|
flashMemLoad = EEPROM.read(flashMemLoad_add); //Load saved autoload (must be excluded from bulk save, uncomment under discretion)
|
||||||
|
if (flashMemLoad == 1) {
|
||||||
|
loadSettings(); //Load stored settings from flash memory
|
||||||
|
}
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
void Onboard_Telemetry() {
|
||||||
|
|
||||||
|
/////////////////////// USB SERIAL DATA TELEMETRY ////////////////////////
|
||||||
|
// 0 - Disable Serial
|
||||||
|
// 1 - Display All
|
||||||
|
// 2 - Display Essential Data
|
||||||
|
// 3 - Display Numbers Only
|
||||||
|
|
||||||
|
currentSerialMillis = millis();
|
||||||
|
if (currentSerialMillis - prevSerialMillis >= millisSerialInterval) { //Run routine every millisRoutineInterval (ms)
|
||||||
|
prevSerialMillis = currentSerialMillis; //Store previous time
|
||||||
|
|
||||||
|
if (serialTelemMode == 0) {}
|
||||||
|
// else if(chargingPause==1){Serial.println("CHARGING PAUSED");} // Charging paused message
|
||||||
|
else if (serialTelemMode == 1) { // 1 - Display All
|
||||||
|
Serial.print(" ERR:"); Serial.print(ERR);
|
||||||
|
Serial.print(" FLV:"); Serial.print(FLV);
|
||||||
|
Serial.print(" BNC:"); Serial.print(BNC);
|
||||||
|
Serial.print(" IUV:"); Serial.print(IUV);
|
||||||
|
Serial.print(" IOC:"); Serial.print(IOC);
|
||||||
|
Serial.print(" OOV:"); Serial.print(OOV);
|
||||||
|
Serial.print(" OOC:"); Serial.print(OOC);
|
||||||
|
Serial.print(" OTE:"); Serial.print(OTE);
|
||||||
|
Serial.print(" REC:"); Serial.print(REC);
|
||||||
|
Serial.print(" MPPTA:"); Serial.print(MPPT_Mode);
|
||||||
|
Serial.print(" CM:"); Serial.print(output_Mode); //Charging Mode
|
||||||
|
|
||||||
|
Serial.print(" ");
|
||||||
|
Serial.print(" BYP:"); Serial.print(bypassEnable);
|
||||||
|
Serial.print(" EN:"); Serial.print(buckEnable);
|
||||||
|
Serial.print(" FAN:"); Serial.print(fanStatus);
|
||||||
|
Serial.print(" WiFi:"); Serial.print(WIFI);
|
||||||
|
Serial.print(" ");
|
||||||
|
Serial.print(" PI:"); Serial.print(powerInput, 3);
|
||||||
|
Serial.print(" PWM:"); Serial.print(PWM);
|
||||||
|
Serial.print(" PPWM:"); Serial.print(PPWM);
|
||||||
|
Serial.print(" VI:"); Serial.print(voltageInput, 3);
|
||||||
|
Serial.print(" VO:"); Serial.print(voltageOutput, 3);
|
||||||
|
Serial.print(" CI:"); Serial.print(currentInput, 3);
|
||||||
|
Serial.print(" CO:"); Serial.print(currentOutput, 3);
|
||||||
|
Serial.print(" Wh:"); Serial.print(Wh, 3);
|
||||||
|
Serial.print(" Temp:"); Serial.print(temperature, 1);
|
||||||
|
Serial.print(" ");
|
||||||
|
Serial.print(" CSMPV:"); Serial.print(currentMidPoint, 3);
|
||||||
|
Serial.print(" CSV:"); Serial.print(CSI_converted, 3);
|
||||||
|
Serial.print(" VO%Dev:"); Serial.print(outputDeviation, 1);
|
||||||
|
Serial.print(" SOC:"); Serial.print(batteryPercent); Serial.print("%");
|
||||||
|
Serial.print(" T:"); Serial.print(secondsElapsed);
|
||||||
|
Serial.print(" LoopT:"); Serial.print(loopTime, 3); Serial.print("ms");
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
else if (serialTelemMode == 2) { // 2 - Display Essential Data
|
||||||
|
Serial.print(" PI:"); Serial.print(powerInput, 3);
|
||||||
|
Serial.print(" PWM:"); Serial.print(PWM);
|
||||||
|
Serial.print(" PPWM:"); Serial.print(PPWM);
|
||||||
|
Serial.print(" VI:"); Serial.print(voltageInput, 3);
|
||||||
|
Serial.print(" VO:"); Serial.print(voltageOutput, 3);
|
||||||
|
Serial.print(" CI:"); Serial.print(currentInput, 3);
|
||||||
|
Serial.print(" CO:"); Serial.print(currentOutput, 3);
|
||||||
|
Serial.print(" Wh:"); Serial.print(Wh, 3);
|
||||||
|
Serial.print(" Temp:"); Serial.print(temperature, 1);
|
||||||
|
Serial.print(" EN:"); Serial.print(buckEnable);
|
||||||
|
Serial.print(" FAN:"); Serial.print(fanStatus);
|
||||||
|
Serial.print(" SOC:"); Serial.print(batteryPercent); Serial.print("%");
|
||||||
|
Serial.print(" T:"); Serial.print(secondsElapsed);
|
||||||
|
Serial.print(" LoopT:"); Serial.print(loopTime, 3); Serial.print("ms");
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
else if (serialTelemMode == 3) { // 3 - Display Numbers Only
|
||||||
|
Serial.print(" "); Serial.print(powerInput, 3);
|
||||||
|
Serial.print(" "); Serial.print(voltageInput, 3);
|
||||||
|
Serial.print(" "); Serial.print(voltageOutput, 3);
|
||||||
|
Serial.print(" "); Serial.print(currentInput, 3);
|
||||||
|
Serial.print(" "); Serial.print(currentOutput, 3);
|
||||||
|
Serial.print(" "); Serial.print(Wh, 3);
|
||||||
|
Serial.print(" "); Serial.print(temperature, 1);
|
||||||
|
Serial.print(" "); Serial.print(buckEnable);
|
||||||
|
Serial.print(" "); Serial.print(fanStatus);
|
||||||
|
Serial.print(" "); Serial.print(batteryPercent);
|
||||||
|
Serial.print(" "); Serial.print(secondsElapsed);
|
||||||
|
Serial.print(" "); Serial.print(loopTime, 3);
|
||||||
|
Serial.print(" "); Serial.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
void setupWiFi() {
|
||||||
|
if (enableWiFi == 1) {
|
||||||
|
//Blynk.begin(auth,ssid,pass);
|
||||||
|
if (blinker_id == "") {
|
||||||
|
if (!AutoWifiConfig())
|
||||||
|
{
|
||||||
|
SmartConfig();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 初始化blinker
|
||||||
|
Blinker.begin(blinker_id, ssid, pass);
|
||||||
|
|
||||||
|
Blinker.attachData(dataRead);
|
||||||
|
Blinker.attachHeartbeat(heartbeat);
|
||||||
|
Blinker.attachDataStorage(dataStorage);
|
||||||
|
}
|
||||||
|
WIFI = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoWifiConfig()
|
||||||
|
{
|
||||||
|
|
||||||
|
//设置固定IP地址
|
||||||
|
//IPAddress staticIP(192, 168, 5, 24); //ESP static ip
|
||||||
|
//IPAddress gateway(192, 168, 5, 1); //IP Address of your WiFi Router (Gateway)
|
||||||
|
//IPAddress subnet(255, 255, 255, 0); //Subnet mask
|
||||||
|
//IPAddress dns(192, 168, 5, 1); //DNS
|
||||||
|
//WiFi.config(staticIP, gateway, subnet, dns);
|
||||||
|
WiFi.begin(ssid, pass); //Wifi接入到网络
|
||||||
|
//WiFi.begin(); //Wifi接入到网络
|
||||||
|
//如果觉得时间太长可改
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
int wstatus = WiFi.status();
|
||||||
|
if (wstatus == WL_CONNECTED)
|
||||||
|
{
|
||||||
|
Serial.println("WIFI AutoConfig Success");
|
||||||
|
Serial.print("LocalIP:");
|
||||||
|
Serial.print(WiFi.localIP());
|
||||||
|
Serial.print(" ,GateIP:");
|
||||||
|
Serial.println(WiFi.gatewayIP());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print("WIFI AutoConfig Waiting......");
|
||||||
|
Serial.println(wstatus);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("WIFI AutoConfig Faild!" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SmartConfig()
|
||||||
|
{
|
||||||
|
int delay_times = 0;
|
||||||
|
// WiFi.mode(WIFI_AP_STA);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
Serial.println("\r\n wait for smartconfig....");
|
||||||
|
WiFi.beginSmartConfig();
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (delay_times > 60) {
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
Serial.print(".");
|
||||||
|
delay(500);
|
||||||
|
if ( WiFi.smartConfigDone())
|
||||||
|
{
|
||||||
|
Serial.println("SmartConfig Success");
|
||||||
|
//Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
|
||||||
|
//Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay_times++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Wireless_Telemetry() {
|
||||||
|
|
||||||
|
////////// WIFI TELEMETRY //////////
|
||||||
|
if (enableWiFi == 1) {
|
||||||
|
int LED1, LED2, LED3, LED4; //Declare LED brightness variable
|
||||||
|
if (buckEnable == 1) {
|
||||||
|
LED1 = 200; //BATTERY CHARGING STATUS
|
||||||
|
} else {
|
||||||
|
LED1 = 0;
|
||||||
|
}
|
||||||
|
if (batteryPercent >= 99 ) {
|
||||||
|
LED2 = 200; //FULL CHARGE STATUS
|
||||||
|
} else {
|
||||||
|
LED2 = 0;
|
||||||
|
}
|
||||||
|
if (batteryPercent <= 10) {
|
||||||
|
LED3 = 200; //LOW BATTERY STATUS
|
||||||
|
} else {
|
||||||
|
LED3 = 0;
|
||||||
|
}
|
||||||
|
if (IUV == 0) {
|
||||||
|
LED4 = 200; //PV INPUT PRESENCE STATUS
|
||||||
|
} else {
|
||||||
|
LED4 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blinker_id == "") {
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(500);
|
||||||
|
AutoWifiConfig();
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Blinker.run();
|
||||||
|
} else {
|
||||||
|
AutoWifiConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Blynk.run();
|
||||||
|
Blynk.virtualWrite(1,powerInput);
|
||||||
|
Blynk.virtualWrite(2,batteryPercent);
|
||||||
|
Blynk.virtualWrite(3,voltageInput);
|
||||||
|
Blynk.virtualWrite(4,currentInput);
|
||||||
|
Blynk.virtualWrite(5,voltageOutput);
|
||||||
|
Blynk.virtualWrite(6,currentOutput);
|
||||||
|
Blynk.virtualWrite(7,temperature);
|
||||||
|
Blynk.virtualWrite(8,Wh/1000);
|
||||||
|
Blynk.virtualWrite(9,energySavings);
|
||||||
|
Blynk.virtualWrite(10,LED1); //LED - Battery Charging Status
|
||||||
|
Blynk.virtualWrite(11,LED2); //LED - Full Battery Charge Status
|
||||||
|
Blynk.virtualWrite(12,LED3); //LED - Low Battery Charge Status
|
||||||
|
Blynk.virtualWrite(13,LED4); //LED - PV Harvesting
|
||||||
|
|
||||||
|
Blynk.virtualWrite(14,voltageBatteryMin); //Minimum Battery Voltage (Read & Write)
|
||||||
|
Blynk.virtualWrite(15,voltageBatteryMax); //Maximum Battery Voltage (Read & Write)
|
||||||
|
Blynk.virtualWrite(16,currentCharging); //Charging Current (Read & Write)
|
||||||
|
Blynk.virtualWrite(17,electricalPrice); //Electrical Price (Write)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
////////// WIFI TELEMETRY //////////
|
||||||
|
if (enableBluetooth == 1) {
|
||||||
|
//ADD BLUETOOTH CODE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,637 @@
|
||||||
|
/* PROJECT FUGU FIRMWARE V1.10 (DIY 1kW Open Source MPPT Solar Charge Controller)
|
||||||
|
By: TechBuilder (Angelo Casimiro)
|
||||||
|
FIRMWARE STATUS: Verified Stable Build Version
|
||||||
|
(Contact me for the experimental beta versions)
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
DATE CREATED: 02/07/2021
|
||||||
|
DATE MODIFIED: 30/08/2021
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
CONTACTS:
|
||||||
|
GitHub - www.github.com/AngeloCasi (New firmware releases will only be available on GitHub Link)
|
||||||
|
Email - casithebuilder@gmail.com
|
||||||
|
YouTube - www.youtube.com/TechBuilder
|
||||||
|
Facebook - www.facebook.com/AngeloCasii
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
PROGRAM FEATURES:
|
||||||
|
- MPPT Perturbed Algorithm With CC-CV
|
||||||
|
- WiFi & Bluetooth BLE Blynk Phone App Telemetry
|
||||||
|
- Selectable Charger/PSU Mode (can operate as a programmable buck converter)
|
||||||
|
- Dual Core ESP32 Unlocked (using xTaskCreatePinnedToCore(); )
|
||||||
|
- Precision ADC Tracking Auto ADS1115/ADS1015 Detection (16bit/12bit I2C ADC)
|
||||||
|
- Automatic ACS712-30A Current Sensor Calibration
|
||||||
|
- Equipped With Battery Disconnect & Input Disconnect Recovery Protection Protocol
|
||||||
|
- LCD Menu Interface (with settings & 4 display layouts)
|
||||||
|
- Flash Memory (non-volatile settings save function)
|
||||||
|
- Settable PWM Resolution (8bit-16bit)
|
||||||
|
- Settable PWM Switching Frequency (1.2kHz - 312kHz)
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
PROGRAM INSTRUCTIONS:
|
||||||
|
1.) Watch YouTube video tutorial first before using
|
||||||
|
2.) Install the required Arduino libraries for the ICs
|
||||||
|
3.) Select Tools > ESP32 Dev Board Board
|
||||||
|
4.) Do not modify code unless you know what you are doing
|
||||||
|
5.) The MPPT's synchronous buck converter topology is code dependent, messing with the algorithm
|
||||||
|
and safety protection protocols can be extremely dangerous especially when dealing with HVDC.
|
||||||
|
6.) Install Blynk Legacy to access the phone app telemetry feature
|
||||||
|
7.) Input the Blynk authentication in this program token sent by Blynk to your email after registration
|
||||||
|
8.) Input WiFi SSID and password in this program
|
||||||
|
9.) When using WiFi only mode, change "disableFlashAutoLoad = 0" to = 1 (LCD and buttons not installed)
|
||||||
|
this prevents the MPPT unit to load the Flash Memory saved settings and will load the Arduino variable
|
||||||
|
declarations set below instead
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
GOOGLE DRIVE PROJECT LINK: coming soon
|
||||||
|
INSTRUCTABLE TUTORIAL LINK: coming soon
|
||||||
|
YOUTUBE TUTORIAL LINK: www.youtube.com/watch?v=ShXNJM6uHLM
|
||||||
|
GITHUB UPDATED FUGU FIRMWARE LINK: github.com/AngeloCasi/FUGU-ARDUINO-MPPT-FIRMWARE
|
||||||
|
-----------------------------------------------------------------------------------------------------------
|
||||||
|
ACTIVE CHIPS USED IN FIRMWARE:
|
||||||
|
- ESP32 WROOM32
|
||||||
|
- ADS1115/ADS1015 I2C ADC
|
||||||
|
- ACS712-30A Current Sensor IC
|
||||||
|
- IR2104 MOSFET Driver
|
||||||
|
- CH340C USB TO UART IC
|
||||||
|
- 16X2 I2C Character LCD
|
||||||
|
|
||||||
|
OTHER CHIPS USED IN PROJECT:
|
||||||
|
- XL7005A 80V 0.4A Buck Regulator (2x)
|
||||||
|
- AMS1115-3.3 LDO Linear Regulator
|
||||||
|
- AMS1115-5.0 LDO Linear Regulator
|
||||||
|
- CSD19505 N-ch MOSFETS (3x)
|
||||||
|
- B1212 DC-DC Isolated Converter
|
||||||
|
- SS310 Diodes
|
||||||
|
*/
|
||||||
|
//================================ MPPT FIRMWARE LCD MENU INFO =====================================//
|
||||||
|
// The lines below are for the Firmware Version info displayed on the MPPT's LCD Menu Interface //
|
||||||
|
//==================================================================================================//
|
||||||
|
String
|
||||||
|
firmwareInfo = "V2.10",
|
||||||
|
firmwareDate = "23/06/2022",
|
||||||
|
firmwareContactR1 = "www.youtube.com/",
|
||||||
|
firmwareContactR2 = "TechBuilder ";
|
||||||
|
|
||||||
|
//====================== ARDUINO LIBRARIES (ESP32 Compatible Libraries) ============================//
|
||||||
|
// You will have to download and install the following libraries below in order to program the MPPT //
|
||||||
|
// unit. Visit TechBuilder's YouTube channel for the "MPPT" tutorial. //
|
||||||
|
//============================================================================================= ====//
|
||||||
|
#include <EEPROM.h> //SYSTEM PARAMETER - EEPROM Library (By: Arduino)
|
||||||
|
#include <Wire.h> //SYSTEM PARAMETER - WIRE Library (By: Arduino)
|
||||||
|
#include <SPI.h> //SYSTEM PARAMETER - SPI Library (By: Arduino)
|
||||||
|
#include <WiFi.h> //SYSTEM PARAMETER - WiFi Library (By: Arduino)
|
||||||
|
#include <WiFiClient.h> //SYSTEM PARAMETER - WiFi Library (By: Arduino)
|
||||||
|
//#include <BlynkSimpleEsp32.h> //SYSTEM PARAMETER - Blynk WiFi Library For Phone App
|
||||||
|
#define BLINKER_WIFI
|
||||||
|
#define BLINKER_ALIGENIE_OUTLET
|
||||||
|
#define BLINKER_WITHOUT_SSL
|
||||||
|
#define BLINKER_ALIGENIE_SENSOR
|
||||||
|
#include <Blinker.h> //#define BLINKER_ESP_SMARTCONFIG
|
||||||
|
#include <LiquidCrystal_I2C.h> //系统参数 - ESP32 LCD 兼容库(作者:Robojax)
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
//#include <Adafruit_ADS1X15.h> //SYSTEM PARAMETER - ADS1115/ADS1015 ADC Library (By: Adafruit)
|
||||||
|
#include <INA226.h>
|
||||||
|
LiquidCrystal_I2C lcd(0x27, 20, 4); //系统参数 - 配置 LCD RowCol 大小和 I2C 地址
|
||||||
|
TaskHandle_t Core2; //SYSTEM PARAMETER - Used for the ESP32 dual core operation
|
||||||
|
//Adafruit_ADS1015 ads; //SYSTEM PARAMETER - ADS1015 ADC Library (By: Adafruit) Kindly delete this line if you are using ADS1115
|
||||||
|
//Adafruit_ADS1115 ads; //SYSTEM PARAMETER - ADS1115 ADC Library (By: Adafruit) Kindly uncomment this if you are using ADS1115
|
||||||
|
INA226 ina1;
|
||||||
|
INA226 ina2;
|
||||||
|
#include <time.h>
|
||||||
|
#define timezone 8
|
||||||
|
|
||||||
|
BlinkerNumber Num1("num-9y2");
|
||||||
|
BlinkerNumber Num2("num-m2k");
|
||||||
|
BlinkerNumber Num3("num-5up");
|
||||||
|
BlinkerNumber Num4("num-ahb");
|
||||||
|
BlinkerNumber Num5("num-l24");
|
||||||
|
BlinkerNumber Num6("num-3bm");
|
||||||
|
BlinkerNumber Num7("num-rgu");
|
||||||
|
BlinkerNumber Num8("num-ao5");
|
||||||
|
BlinkerNumber Num9("num-ycz");
|
||||||
|
|
||||||
|
BlinkerButton Button1("btn-e6n");
|
||||||
|
BlinkerButton Button2("btn-e3t");
|
||||||
|
BlinkerButton Button3("btn-ogw");
|
||||||
|
BlinkerButton Button4("btn-q4l");
|
||||||
|
|
||||||
|
|
||||||
|
//====================================== USER PARAMETERS ===========================================//
|
||||||
|
//下面的参数是没有MPPT充电器设置时使用的默认参数 //
|
||||||
|
//通过 LCD 菜单界面或手机 WiFi 应用程序设置或保存。这里的一些参数//
|
||||||
|
//将允许您覆盖或解锁高级用户的功能(不在 LCD 菜单上的设置)//
|
||||||
|
//==================================================================================================//
|
||||||
|
#define backflow_MOSFET 27 //SYSTEM PARAMETER - Backflow MOSFET
|
||||||
|
#define buck_IN 33 //SYSTEM PARAMETER - Buck MOSFET Driver PWM Pin
|
||||||
|
#define buck_EN 32 //SYSTEM PARAMETER - Buck MOSFET Driver Enable Pin
|
||||||
|
#define LED 2 //SYSTEM PARAMETER - LED Indicator GPIO Pin
|
||||||
|
#define FAN 16 //SYSTEM PARAMETER - Fan GPIO Pin
|
||||||
|
//#define ADC_ALERT 35 //SYSTEM PARAMETER - ADC_ALERT GPIO Pin
|
||||||
|
#define INA1_ALERT 35 //SYSTEM PARAMETER - INA226 Alert GPIO Pin
|
||||||
|
#define INA2_ALERT 34 //SYSTEM PARAMETER - INA226 Alert GPIO Pin
|
||||||
|
#define TempSensor 39 //SYSTEM PARAMETER - Temperature Sensor GPIO Pin
|
||||||
|
#define buttonLeft 18 //SYSTEM PARAMETER -
|
||||||
|
#define buttonRight 19 //SYSTEM PARAMETER -
|
||||||
|
#define buttonBack 17 //SYSTEM PARAMETER -
|
||||||
|
#define buttonSelect 23 //SYSTEM PARAMETER -
|
||||||
|
|
||||||
|
#define eeprom_size 4096
|
||||||
|
|
||||||
|
#define MPPT_Mode_add 2448 // charging mode setting
|
||||||
|
#define voltageBatteryMax_add 2449 // Max Battery Voltage (whole)
|
||||||
|
#define voltageBatteryMax2_add 2450 // Max Battery Voltage (decimal)
|
||||||
|
#define voltageBatteryMin_add 2451 // Min Battery Voltage (whole)
|
||||||
|
#define voltageBatteryMin2_add 2452 // Min Battery Voltage (decimal)
|
||||||
|
#define currentCharging_add 2453 // Charging Current (whole)
|
||||||
|
#define currentCharging2_add 2454 // Charging Current (decimal)
|
||||||
|
#define enableFan_add 2455 // Fan Enable (Bool)
|
||||||
|
#define temperatureFan_add 2456 // Fan Temp (Integer)
|
||||||
|
#define temperatureMax_add 2457 // Shutdown Temp (Integer)
|
||||||
|
#define enableWiFi_add 2458 // Enable WiFi (Boolean)
|
||||||
|
#define flashMemLoad_add 2459 // Enable autoload (on by default)
|
||||||
|
#define output_Mode_add 2460 // Charger/PSU Mode Selection (1 = Charger Mode)
|
||||||
|
#define backlightSleepMode_add 2461 // LCD backlight sleep timer (default: 0 = never)
|
||||||
|
|
||||||
|
//========================================= WiFi SSID ==============================================//
|
||||||
|
// This MPPT firmware uses the Blynk phone app and arduino library for controls and data telemetry //
|
||||||
|
// Fill in your WiFi SSID and password. You will also have to get your own authentication token //
|
||||||
|
// from email after registering from the Blynk platform. //
|
||||||
|
//==================================================================================================//
|
||||||
|
char
|
||||||
|
auth[] = "InputBlynkAuthenticationToken", // USER PARAMETER - Input Blynk Authentication Token (From email after registration)
|
||||||
|
ssid[] = "", // USER PARAMETER - Enter Your WiFi SSID
|
||||||
|
pass[] = ""; // USER PARAMETER - Enter Your WiFi Password
|
||||||
|
const char *blinker_id = ""; //Blinker ID
|
||||||
|
//====================================== USER PARAMETERS ==========================================//
|
||||||
|
//下面的参数是没有MPPT充电器设置时使用的默认参数 //
|
||||||
|
//通过 LCD 菜单界面或手机 WiFi 应用程序设置或保存。这里的一些参数//
|
||||||
|
//将允许您覆盖或解锁高级用户的功能(不在 LCD 菜单上的设置)//
|
||||||
|
//=================================================================================================//
|
||||||
|
bool
|
||||||
|
MPPT_Mode = 1, // USER PARAMETER - 启用 MPPT 算法,当禁用充电器时使用 CC-CV 算法
|
||||||
|
output_Mode = 1, // USER PARAMETER - 0 = PSU 模式, 1 = 充电器模式
|
||||||
|
disableFlashAutoLoad = 0, // USER PARAMETER - 强制 MPPT 不使用闪存保存的设置,启用此“1”默认为已编程的固件设置
|
||||||
|
enablePPWM = 1, // USER PARAMETER - 启用预测 PWM,这加快了调节速度(仅适用于电池充电应用)
|
||||||
|
enableWiFi = 1, // USER PARAMETER - 启用 WiFi 连接
|
||||||
|
enableFan = 1, // USER PARAMETER - 启用冷却风扇
|
||||||
|
enableBluetooth = 1, // USER PARAMETER - 启用蓝牙连
|
||||||
|
enableLCD = 1, // USER PARAMETER - 启用 接LCD 显示
|
||||||
|
enableLCDBacklight = 1, // USER PARAMETER - 启用 LCD 显示器的背光
|
||||||
|
overrideFan = 0, // USER PARAMETER - 风扇始终开启
|
||||||
|
enableDynamicCooling = 0; // USER PARAMETER - 启用 PWM 冷却控制
|
||||||
|
int
|
||||||
|
serialTelemMode = 1, // USER PARAMETER - 选择串行遥测数据馈送(0 - 禁用串行,1 - 显示所有数据,2 - 显示基本,3 - 仅数字)
|
||||||
|
pwmResolution = 11, // USER PARAMETER - PWM 位分辨率
|
||||||
|
pwmFrequency = 39000, // USER PARAMETER - PWM 开关频率 - Hz(用于降压)
|
||||||
|
temperatureFan = 60, // USER PARAMETER - 风扇开启的温度阈值
|
||||||
|
temperatureMax = 90, // USER PARAMETER - 过热,超过时系统关闭(摄氏度)
|
||||||
|
telemCounterReset = 0, // USER PARAMETER - 每隔一次重置 Telem 数据(0 = 从不,1 = 日,2 = 周,3 = 月,4 = 年)
|
||||||
|
errorTimeLimit = 1000, // USER PARAMETER - 重置错误计数器的时间间隔(毫秒)
|
||||||
|
errorCountLimit = 5, // USER PARAMETER - 最大错误数
|
||||||
|
millisRoutineInterval = 250, // USER PARAMETER - 例程函数的时间间隔刷新率 (ms)
|
||||||
|
millisSerialInterval = 5, // USER PARAMETER - USB 串行数据馈送的时间间隔刷新率 (ms)
|
||||||
|
millisLCDInterval = 1000, // USER PARAMETER - LCD 显示器的时间间隔刷新率 (ms)
|
||||||
|
millisWiFiInterval = 1000, // USER PARAMETER - WiFi 遥测的时间间隔刷新率 (ms)
|
||||||
|
millisLCDBackLInterval = 1000, // USER PARAMETER - 用户参数 - WiFi 遥测的时间间隔刷新率 (ms)
|
||||||
|
backlightSleepMode = 2, // USER PARAMETER - - 0 = 从不, 1 = 10 秒, 2 = 5 分钟, 3 = 1 小时, 4 = 6 小时, 5 = 12 小时, 6 = 1 天, 7 = 3 天, 8 = 1 周, 9 = 1个月
|
||||||
|
baudRate = 500000; // 用户参数 - USB 串行波特率 (bps)
|
||||||
|
|
||||||
|
float
|
||||||
|
voltageBatteryMax = 12.6000, // USER PARAMETER - 最大电池充电电压(输出 V)
|
||||||
|
voltageBatteryMin = 3.0000, // USER PARAMETER - 最小电池充电电压(输出 V)
|
||||||
|
currentCharging = 1.0000, // USER PARAMETER - 最大充电电流(A - 输出)
|
||||||
|
electricalPrice = 0.6000; // USER PARAMETER - 每千瓦时的输入电价
|
||||||
|
|
||||||
|
|
||||||
|
//================================== CALIBRATION PARAMETERS =======================================//
|
||||||
|
//可以调整以下参数以设计您自己的 MPPT 充电控制器。只修改 //
|
||||||
|
//如果你知道你在做什么,下面的值。以下值已针对 // 进行了预校准
|
||||||
|
// TechBuilder (Angelo S. Casimiro) 设计的 MPPT 充电控制器 // //
|
||||||
|
//=================================================================================================//
|
||||||
|
const bool
|
||||||
|
ADS1015_Mode = 1; // CALIB PARAMETER - Use 1 for ADS1015 ADC model use 0 for ADS1115 ADC model
|
||||||
|
const int
|
||||||
|
ADC_GainSelect = 2, // 校准参数 - ADC 增益选择 (0→±6.144V 3mV/bit, 1→±4.096V 2mV/bit, 2→±2.048V 1mV/bit)
|
||||||
|
avgCountVS = 3, // 校准参数 - 电压传感器平均采样计数(推荐:3)
|
||||||
|
avgCountCS = 4, // 校准参数 - 电流传感器平均采样计数(推荐:4)
|
||||||
|
avgCountTS = 500; // 校准参数 - 温度传感器平均采样计数
|
||||||
|
float
|
||||||
|
inVoltageDivRatio = 2.9784, // 校准参数 - 输入分压器传感器比率(更改此值以校准电压传感器)
|
||||||
|
outVoltageDivRatio = 2.8275, // 校准参数 - 输出分压器传感器比率(更改此值以校准电压传感器)
|
||||||
|
vOutSystemMax = 80.0000, // 校准参数 - 最大输入电压
|
||||||
|
cOutSystemMax = 50.0000, // 校准参数 - 最大输出电压
|
||||||
|
ntcResistance = 10000.00, // 校准参数 - NTC 温度传感器的电阻。如果您使用 10k NTC,请更改为 10000.00
|
||||||
|
voltageDropout = 1.0000, // 校准参数 - 降压稳压器的压降电压(由于最大占空比限制而存在 DOV)
|
||||||
|
voltageBatteryThresh = 1.5000, // 校准参数 - 达到此电压时断电(输出 V)
|
||||||
|
currentInAbsolute = 31.0000, // 校准参数 - 系统可以处理的最大输入电流(A - 输入)
|
||||||
|
currentOutAbsolute = 50.0000, // 校准参数 - 系统可以处理的最大输出电流(A - 输入)
|
||||||
|
PPWM_margin = 99.5000, // 校准参数 - 预测 PWM 的最小工作占空比 (%)
|
||||||
|
PWM_MaxDC = 97.0000, // 校准参数 - 最大工作占空比 (%) 90%-97% 是好的
|
||||||
|
efficiencyRate = 1.0000, // 校准参数 - 理论降压效率(十进制百分比)
|
||||||
|
currentMidPoint = 2.5250, // 校准参数 - 电流传感器中点 (V)
|
||||||
|
currentSens = 0.0000, // 校准参数 - 电流传感器灵敏度 (V/A)
|
||||||
|
currentSensV = 0.0330, // 校准参数 - 电流传感器灵敏度 (mV/A)
|
||||||
|
vInSystemMin = 8.000; // 校准参数 - 系统识别最低电压
|
||||||
|
|
||||||
|
//===================================== SYSTEM PARAMETERS =========================================//
|
||||||
|
//不要更改本节中的参数值。下面的值是系统使用的变量 //
|
||||||
|
//进程。更改值可能会损坏 MPPT 硬件。请保持原样!然而, //
|
||||||
|
//您可以访问这些变量来获取您的模组所需的数据。//
|
||||||
|
//=================================================================================================//
|
||||||
|
bool
|
||||||
|
buckEnable = 0, // SYSTEM PARAMETER - Buck Enable Status
|
||||||
|
fanStatus = 0, // SYSTEM PARAMETER - Fan activity status (1 = On, 0 = Off)
|
||||||
|
bypassEnable = 0, // SYSTEM PARAMETER -
|
||||||
|
chargingPause = 0, // SYSTEM PARAMETER -
|
||||||
|
lowPowerMode = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonRightStatus = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonLeftStatus = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonBackStatus = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonSelectStatus = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonRightCommand = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonLeftCommand = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonBackCommand = 0, // SYSTEM PARAMETER -
|
||||||
|
buttonSelectCommand = 0, // SYSTEM PARAMETER -
|
||||||
|
settingMode = 0, // SYSTEM PARAMETER -
|
||||||
|
setMenuPage = 0, // SYSTEM PARAMETER -
|
||||||
|
boolTemp = 0, // SYSTEM PARAMETER -
|
||||||
|
flashMemLoad = 0, // SYSTEM PARAMETER -
|
||||||
|
confirmationMenu = 0, // SYSTEM PARAMETER -
|
||||||
|
WIFI = 0, // SYSTEM PARAMETER -
|
||||||
|
BNC = 0, // SYSTEM PARAMETER -
|
||||||
|
REC = 0, // SYSTEM PARAMETER -
|
||||||
|
FLV = 0, // SYSTEM PARAMETER -
|
||||||
|
IUV = 0, // SYSTEM PARAMETER -
|
||||||
|
IOV = 0, // SYSTEM PARAMETER -
|
||||||
|
IOC = 0, // SYSTEM PARAMETER -
|
||||||
|
OUV = 0, // SYSTEM PARAMETER -
|
||||||
|
OOV = 0, // SYSTEM PARAMETER -
|
||||||
|
OOC = 0, // SYSTEM PARAMETER -
|
||||||
|
OTE = 0; // SYSTEM PARAMETER -
|
||||||
|
int
|
||||||
|
inputSource = 0, // SYSTEM PARAMETER - 0 = MPPT 没有电源,1 = MPPT 使用太阳能作为电源,2 = MPPT 使用电池作为电源
|
||||||
|
avgStoreTS = 0, // SYSTEM PARAMETER - 温度传感器使用非侵入式平均,这是用于平均平均的累加器
|
||||||
|
temperature = 0, // SYSTEM PARAMETER -
|
||||||
|
sampleStoreTS = 0, // SYSTEM PARAMETER - TS AVG 第 n 个样本
|
||||||
|
pwmMax = 0, // SYSTEM PARAMETER -
|
||||||
|
pwmMaxLimited = 0, // SYSTEM PARAMETER -
|
||||||
|
PWM = 0, // SYSTEM PARAMETER -
|
||||||
|
PPWM = 0, // SYSTEM PARAMETER -
|
||||||
|
pwmChannel = 0, // SYSTEM PARAMETER -
|
||||||
|
batteryPercent = 0, // SYSTEM PARAMETER -
|
||||||
|
errorCount = 0, // SYSTEM PARAMETER -
|
||||||
|
menuPage = 0, // SYSTEM PARAMETER -
|
||||||
|
subMenuPage = 0, // SYSTEM PARAMETER -
|
||||||
|
ERR = 0, // SYSTEM PARAMETER -
|
||||||
|
conv1 = 0, // SYSTEM PARAMETER -
|
||||||
|
conv2 = 0, // SYSTEM PARAMETER -
|
||||||
|
intTemp = 0; // SYSTEM PARAMETER -
|
||||||
|
float
|
||||||
|
VSI = 0.0000, // SYSTEM PARAMETER - 原始输入电压传感器 ADC 电压
|
||||||
|
VSO = 0.0000, // SYSTEM PARAMETER - 原始输出电压传感器 ADC 电压
|
||||||
|
CSI = 0.0000, // SYSTEM PARAMETER - 原始电流传感器 ADC 电压
|
||||||
|
CSO = 0.0000, // SYSTEM PARAMETER - Raw current sensor ADC voltage
|
||||||
|
CSI_converted = 0.0000, // SYSTEM PARAMETER - 实际电流传感器 ADC 电压
|
||||||
|
CSO_converted = 0.0000, // SYSTEM PARAMETER - Actual current sensor ADC voltage
|
||||||
|
TS = 0.0000, // SYSTEM PARAMETER - 原始温度传感器 ADC 值
|
||||||
|
powerInput = 0.0000, // SYSTEM PARAMETER - 输入功率(太阳能)以瓦特为单位
|
||||||
|
powerInputPrev = 0.0000, // SYSTEM PARAMETER - 先前存储的 MPPT 算法的输入功率变量(瓦特)
|
||||||
|
powerOutput = 0.0000, // SYSTEM PARAMETER - 输出功率(电池或充电功率,以瓦特为单位)
|
||||||
|
energySavings = 0.0000, // SYSTEM PARAMETER - 法定货币(比索、美元、欧元等)的能源节约
|
||||||
|
voltageInput = 0.0000, // SYSTEM PARAMETER - 输入电压(太阳能电压)
|
||||||
|
voltageInputPrev = 0.0000, // SYSTEM PARAMETER - 先前存储的 MPPT 算法的输入电压变量
|
||||||
|
voltageOutput = 0.0000, // SYSTEM PARAMETER - 输入电压(电池电压)
|
||||||
|
currentInput = 0.0000, // SYSTEM PARAMETER - 输出功率(电池或充电电压)
|
||||||
|
currentOutput = 0.0000, // SYSTEM PARAMETER - 输出电流(电池或充电电流,以安培为单位)
|
||||||
|
TSlog = 0.0000, // SYSTEM PARAMETER - NTC 热敏电阻热感应代码的一部分
|
||||||
|
ADC_BitReso = 0.0000, // SYSTEM PARAMETER - 系统检测 ADS1015/ADS1115 ADC 的适当位分辨率因子
|
||||||
|
daysRunning = 0.0000, // SYSTEM PARAMETER - 存储 MPPT 设备自上次通电以来运行的总天数
|
||||||
|
Wh = 0.0000, // SYSTEM PARAMETER - 存储收集到的累积能量(瓦特小时)
|
||||||
|
kWh = 0.0000, // SYSTEM PARAMETER - 存储收集到的累积能量(千瓦时)
|
||||||
|
MWh = 0.0000, // SYSTEM PARAMETER - 存储收集到的累积能量(兆瓦时)
|
||||||
|
loopTime = 0.0000, // SYSTEM PARAMETER -
|
||||||
|
outputDeviation = 0.0000, // SYSTEM PARAMETER - 输出电压偏差 (%)
|
||||||
|
buckEfficiency = 0.0000, // SYSTEM PARAMETER - 测量降压转换器功率转换效率(仅适用于我的双电流传感器版本)
|
||||||
|
floatTemp = 0.0000,
|
||||||
|
vOutSystemMin = 0.0000; // CALIB PARAMETER -
|
||||||
|
unsigned long
|
||||||
|
currentErrorMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentButtonMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentSerialMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentRoutineMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentLCDMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentLCDBackLMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentWiFiMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
currentMenuSetMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevButtonMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevSerialMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevRoutineMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevErrorMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevWiFiMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevLCDMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
prevLCDBackLMillis = 0, //SYSTEM PARAMETER -
|
||||||
|
timeOn = 0, //SYSTEM PARAMETER -
|
||||||
|
loopTimeStart = 0, //SYSTEM PARAMETER - 用于循环循环秒表,记录循环开始时间
|
||||||
|
loopTimeEnd = 0, //SYSTEM PARAMETER - 用于循环循环秒表,记录循环结束时间
|
||||||
|
secondsElapsed = 0; //SYSTEM PARAMETER -
|
||||||
|
|
||||||
|
//====================================== MAIN PROGRAM =============================================//
|
||||||
|
// The codes below contain all the system processes for the MPPT firmware. Most of them are called //
|
||||||
|
// from the 8 .ino tabs. The codes are too long, Arduino tabs helped me a lot in organizing them. //
|
||||||
|
// The firmware runs on two cores of the Arduino ESP32 as seen on the two separate pairs of void //
|
||||||
|
// setups and loops. The xTaskCreatePinnedToCore() freeRTOS function allows you to access the //
|
||||||
|
// unused ESP32 core through Arduino. Yes it does multicore processes simultaneously! //
|
||||||
|
//=================================================================================================//
|
||||||
|
|
||||||
|
void dataRead(const String &data)
|
||||||
|
{
|
||||||
|
BLINKER_LOG("Blinker readString: ", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void heartbeat()
|
||||||
|
{
|
||||||
|
Num1.print(powerInput);
|
||||||
|
Num2.print(voltageInput);
|
||||||
|
Num3.print(currentInput);
|
||||||
|
Num4.print(batteryPercent);
|
||||||
|
Num5.print(voltageOutput);
|
||||||
|
Num6.print(currentOutput);
|
||||||
|
Num7.print(Wh/1000);
|
||||||
|
Num8.print(electricalPrice);
|
||||||
|
Num9.print(temperature);
|
||||||
|
|
||||||
|
if(buckEnable==1){Button1.print("off");}else{Button1.print("on");}
|
||||||
|
if(batteryPercent >= 99){Button2.print("off");}else{Button2.print("on");}
|
||||||
|
if(batteryPercent <= 10){Button3.print("off");}else{Button3.print("on");}
|
||||||
|
if(IUV == 0){Button4.print("off");}else{Button4.print("on");}
|
||||||
|
|
||||||
|
}
|
||||||
|
void dataStorage()
|
||||||
|
{
|
||||||
|
Blinker.dataStorage("data-1", powerInput); //数据组件名,数据值
|
||||||
|
Blinker.dataStorage("data-2", voltageInput);
|
||||||
|
Blinker.dataStorage("data-3", currentInput);
|
||||||
|
Blinker.dataStorage("data-4", powerOutput);
|
||||||
|
Blinker.dataStorage("data-5", voltageOutput);
|
||||||
|
Blinker.dataStorage("data-6", currentOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
//================= CORE0: SETUP (DUAL CORE MODE) =====================//
|
||||||
|
void coreTwo(void * pvParameters) {
|
||||||
|
setupWiFi(); //TAB#7 - WiFi Initialization
|
||||||
|
//================= CORE0: LOOP (DUAL CORE MODE) ======================//
|
||||||
|
while (1) {
|
||||||
|
Wireless_Telemetry(); //TAB#7 - Wireless telemetry (WiFi & Bluetooth)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//================== CORE1: SETUP (DUAL CORE MODE) ====================//
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
//SERIAL INITIALIZATION
|
||||||
|
Serial.begin(baudRate); //Set serial baud rate
|
||||||
|
Serial.println("> Serial Initialized"); //Startup message
|
||||||
|
BLINKER_DEBUG.stream(Serial);
|
||||||
|
|
||||||
|
//GPIO PIN INITIALIZATION
|
||||||
|
pinMode(backflow_MOSFET, OUTPUT);
|
||||||
|
pinMode(buck_EN, OUTPUT);
|
||||||
|
pinMode(LED, OUTPUT);
|
||||||
|
pinMode(FAN, OUTPUT);
|
||||||
|
pinMode(TS, INPUT);
|
||||||
|
//pinMode(ADC_ALERT,INPUT);
|
||||||
|
pinMode(INA1_ALERT, INPUT);
|
||||||
|
pinMode(INA2_ALERT, INPUT);
|
||||||
|
pinMode(buttonLeft, INPUT);
|
||||||
|
pinMode(buttonRight, INPUT);
|
||||||
|
pinMode(buttonBack, INPUT);
|
||||||
|
pinMode(buttonSelect, INPUT);
|
||||||
|
|
||||||
|
//PWM INITIALIZATION
|
||||||
|
ledcSetup(pwmChannel, pwmFrequency, pwmResolution); //Set PWM Parameters
|
||||||
|
ledcAttachPin(buck_IN, pwmChannel); //Set pin as PWM
|
||||||
|
ledcWrite(pwmChannel, PWM); //Write PWM value at startup (duty = 0)
|
||||||
|
pwmMax = pow(2, pwmResolution) - 1; //Get PWM Max Bit Ceiling
|
||||||
|
pwmMaxLimited = (PWM_MaxDC * pwmMax) / 100.000; //Get maximum PWM Duty Cycle (pwm limiting protection)
|
||||||
|
|
||||||
|
//ADC INITIALIZATION
|
||||||
|
//ADC_SetGain(); //Sets ADC Gain & Range
|
||||||
|
//ads.begin(); //Initialize ADC
|
||||||
|
Serial.println("Initialize INA226 Slave1 0x40 A1 GND A0 GND");
|
||||||
|
Serial.println("-----------------------------------------------");
|
||||||
|
|
||||||
|
// Default INA226 address is 0x40
|
||||||
|
ina1.begin();
|
||||||
|
|
||||||
|
// Configure INA226
|
||||||
|
ina1.configure(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_588US, INA226_SHUNT_CONV_TIME_588US, INA226_MODE_SHUNT_BUS_CONT);
|
||||||
|
|
||||||
|
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
|
||||||
|
ina1.calibrate(0.002, 40);
|
||||||
|
|
||||||
|
// Display configuration
|
||||||
|
checkConfig(ina1);
|
||||||
|
|
||||||
|
Serial.println("-----------------------------------------------");
|
||||||
|
|
||||||
|
Serial.println("Initialize INA226 Slave2 0x41");
|
||||||
|
Serial.println("-----------------------------------------------");
|
||||||
|
|
||||||
|
// Default INA226 address is 0x40
|
||||||
|
ina2.begin(0x41);
|
||||||
|
|
||||||
|
// Configure INA226
|
||||||
|
ina2.configure(INA226_AVERAGES_4, INA226_BUS_CONV_TIME_588US, INA226_SHUNT_CONV_TIME_588US, INA226_MODE_SHUNT_BUS_CONT);
|
||||||
|
|
||||||
|
// Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A
|
||||||
|
ina2.calibrate(0.002, 40);
|
||||||
|
|
||||||
|
// Display configuration
|
||||||
|
checkConfig(ina2);
|
||||||
|
|
||||||
|
Serial.println("-----------------------------------------------");
|
||||||
|
|
||||||
|
//GPIO INITIALIZATION
|
||||||
|
buck_Disable();
|
||||||
|
|
||||||
|
//ENABLE DUAL CORE MULTITASKING
|
||||||
|
xTaskCreatePinnedToCore(coreTwo, "coreTwo", 10000, NULL, 0, &Core2, 0);
|
||||||
|
|
||||||
|
//INITIALIZE AND LIOAD FLASH MEMORY DATA
|
||||||
|
EEPROM.begin(eeprom_size);
|
||||||
|
Serial.println("> FLASH MEMORY: STORAGE INITIALIZED"); //Startup message
|
||||||
|
initializeFlashAutoload(); //Load stored settings from flash memory
|
||||||
|
Serial.println("> FLASH MEMORY: SAVED DATA LOADED"); //Startup message
|
||||||
|
|
||||||
|
//LCD INITIALIZATION
|
||||||
|
if (enableLCD == 1) {
|
||||||
|
lcd.begin(16, 2);
|
||||||
|
lcd.setBacklight(HIGH);
|
||||||
|
lcd.setCursor(0, 0);
|
||||||
|
lcd.print("MPPT INIT");
|
||||||
|
lcd.setCursor(0, 1);
|
||||||
|
lcd.print("FIRMWARE ");
|
||||||
|
lcd.print(firmwareInfo);
|
||||||
|
delay(1500);
|
||||||
|
lcd.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t chipId = 0;
|
||||||
|
for (int i = 0; i < 17; i = i + 8) {
|
||||||
|
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
|
||||||
|
}
|
||||||
|
Serial.printf("Chip ID: %d\r\n", chipId);
|
||||||
|
|
||||||
|
Serial.printf("ESP32 Chip ID = %04X", (uint16_t)(ESP.getEfuseMac() >> 32)); //print High 2 bytes
|
||||||
|
Serial.printf("%08X\r\n", (uint32_t)ESP.getEfuseMac()); //print Low 4bytes.
|
||||||
|
|
||||||
|
Serial.printf("Chip model = %s Rev %d\r\n", ESP.getChipModel(), ESP.getChipRevision());
|
||||||
|
Serial.printf("This chip has %d cores CpuFreqMHz = %u\r\n", ESP.getChipCores(), ESP.getCpuFreqMHz());
|
||||||
|
Serial.printf("get Cycle Count = %u\r\n", ESP.getCycleCount());
|
||||||
|
Serial.printf("SDK version:%s\r\n", ESP.getSdkVersion()); //获取IDF版本
|
||||||
|
|
||||||
|
//获取片内内存 Internal RAM
|
||||||
|
Serial.printf("Total heap size = %u\t", ESP.getHeapSize());
|
||||||
|
Serial.printf("Available heap = %u\r\n", ESP.getFreeHeap());
|
||||||
|
Serial.printf("Lowest level of free heap since boot = %u\r\n", ESP.getMinFreeHeap());
|
||||||
|
Serial.printf("Largest block of heap that can be allocated at once = %u\r\n", ESP.getMaxAllocHeap());
|
||||||
|
|
||||||
|
//SPI RAM
|
||||||
|
Serial.printf("Total Psram size = %u\t", ESP.getPsramSize());
|
||||||
|
Serial.printf("Available Psram = %u\r\n", ESP.getFreePsram());
|
||||||
|
Serial.printf("Lowest level of free Psram since boot = %u\r\n", ESP.getMinFreePsram());
|
||||||
|
Serial.printf("Largest block of Psram that can be allocated at once = %u\r\n", ESP.getMinFreePsram());
|
||||||
|
|
||||||
|
byte mac[6];
|
||||||
|
WiFi.macAddress(mac);
|
||||||
|
printf("macAddress 0x%02X:0x%02X:0x%02X:0x%02X:0x%02X:0x%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
//以下是启动OTA,可以通过WiFi刷新固件
|
||||||
|
ArduinoOTA.setHostname("ESP32_MPPT");
|
||||||
|
// No authentication by default
|
||||||
|
// ArduinoOTA.setPassword("admin");
|
||||||
|
|
||||||
|
// Password can be set with it's md5 value as well
|
||||||
|
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||||
|
ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||||
|
ArduinoOTA.onStart([]() {
|
||||||
|
String type;
|
||||||
|
if (ArduinoOTA.getCommand() == U_FLASH) {
|
||||||
|
type = "sketch";
|
||||||
|
} else { // U_SPIFFS
|
||||||
|
type = "filesystem";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||||
|
Serial.println("Start updating " + type);
|
||||||
|
});
|
||||||
|
ArduinoOTA.onEnd([]() {
|
||||||
|
Serial.println("\nEnd");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
|
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||||
|
});
|
||||||
|
ArduinoOTA.onError([](ota_error_t error) {
|
||||||
|
Serial.printf("Error[%u]: ", error);
|
||||||
|
if (error == OTA_AUTH_ERROR) {
|
||||||
|
Serial.println("Auth Failed");
|
||||||
|
} else if (error == OTA_BEGIN_ERROR) {
|
||||||
|
Serial.println("Begin Failed");
|
||||||
|
} else if (error == OTA_CONNECT_ERROR) {
|
||||||
|
Serial.println("Connect Failed");
|
||||||
|
} else if (error == OTA_RECEIVE_ERROR) {
|
||||||
|
Serial.println("Receive Failed");
|
||||||
|
} else if (error == OTA_END_ERROR) {
|
||||||
|
Serial.println("End Failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
Serial.println("OTA Ready");
|
||||||
|
|
||||||
|
|
||||||
|
//设置时间格式以及时间服务器的网址
|
||||||
|
configTime(timezone * 3600, 0, "pool.ntp.org", "time.nist.gov");
|
||||||
|
Serial.println("\nWaiting for time");
|
||||||
|
while (!time(nullptr)) {
|
||||||
|
Serial.print(".");
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//SETUP FINISHED
|
||||||
|
Serial.println("> MPPT HAS INITIALIZED"); //Startup message
|
||||||
|
|
||||||
|
}
|
||||||
|
unsigned long TenthSecondsSinceStart = 0;
|
||||||
|
unsigned long LastMillis = 0;
|
||||||
|
char DateTimeStr[20];
|
||||||
|
|
||||||
|
void OnSecond()
|
||||||
|
{
|
||||||
|
time_t now = time(nullptr); //获取当前时间
|
||||||
|
|
||||||
|
//转换成年月日的数字,可以更加自由的显示。
|
||||||
|
struct tm* timenow;
|
||||||
|
timenow = localtime(&now);
|
||||||
|
unsigned char tempHour = timenow->tm_hour;
|
||||||
|
unsigned char tempMinute = timenow->tm_min;
|
||||||
|
unsigned char tempSecond = timenow->tm_sec;
|
||||||
|
unsigned char tempDay = timenow->tm_mday;
|
||||||
|
unsigned char tempMonth = timenow->tm_mon + 1;
|
||||||
|
unsigned int tempYear = timenow->tm_year + 1900;
|
||||||
|
unsigned char tempWeek = timenow->tm_wday;
|
||||||
|
|
||||||
|
//生成 年月日时分秒 字符串。
|
||||||
|
sprintf(DateTimeStr, "%d-%02d-%02d %02d:%02d:%02d"
|
||||||
|
, tempYear
|
||||||
|
, tempMonth
|
||||||
|
, tempDay
|
||||||
|
, tempHour
|
||||||
|
, tempMinute
|
||||||
|
, tempSecond
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTenthSecond() // 100ms 十分之一秒
|
||||||
|
{
|
||||||
|
|
||||||
|
if (TenthSecondsSinceStart % 3 == 0) //0.3S刷新数据,
|
||||||
|
{
|
||||||
|
//读取采样数据1
|
||||||
|
|
||||||
|
//voltageInput = ina1.readBusVoltage()*(R1_VOLTAGE1+R2_VOLTAGE1)/R1_VOLTAGE1;
|
||||||
|
//voltageOutput = ina2.readBusVoltage()*(R1_VOLTAGE2+R2_VOLTAGE2)/R1_VOLTAGE2;
|
||||||
|
|
||||||
|
//读取采样数据2
|
||||||
|
|
||||||
|
//CSI_converted = ina1.readShuntCurrent();
|
||||||
|
//CSO_converted = ina2.readShuntCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TenthSecondsSinceStart % 10 == 0) //10次为1秒
|
||||||
|
{
|
||||||
|
OnSecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TenthSecondsSinceStartTask() //100ms
|
||||||
|
{
|
||||||
|
unsigned long CurrentMillis = millis();
|
||||||
|
if (abs(int(CurrentMillis - LastMillis)) > 100)
|
||||||
|
{
|
||||||
|
LastMillis = CurrentMillis;
|
||||||
|
TenthSecondsSinceStart++;
|
||||||
|
OnTenthSecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//================== CORE1: LOOP (DUAL CORE MODE) ======================//
|
||||||
|
void loop() {
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
|
||||||
|
TenthSecondsSinceStartTask();
|
||||||
|
|
||||||
|
Read_Sensors(); //TAB#2 - Sensor data measurement and computation
|
||||||
|
Device_Protection(); //TAB#3 - Fault detection algorithm
|
||||||
|
System_Processes(); //TAB#4 - Routine system processes
|
||||||
|
Charging_Algorithm(); //TAB#5 - Battery Charging Algorithm
|
||||||
|
Onboard_Telemetry(); //TAB#6 - Onboard telemetry (USB & Serial Telemetry)
|
||||||
|
LCD_Menu(); //TAB#8 - Low Power Algorithm
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{¨version¨¨2.0.0¨¨config¨{¨headerColor¨¨transparent¨¨headerStyle¨¨dark¨¨background¨{¨img¨¨assets/img/bg/2.jpg¨}}¨dashboard¨|{¨type¨¨num¨¨t0¨¨太阳能功率¨¨ico¨¨fal fa-question¨¨clr¨¨#00A90C¨¨min¨É¨max¨¢1c¨uni¨´W´¨bg¨É¨cols¨Í¨rows¨Ë¨key¨¨num-9y2¨´x´É´y´Ë¨lstyle¨Ë¨rt¨»}{ßBßCßD¨累计功率¨ßF¨fad fa-tint¨ßH¨#FBA613¨ßJÉßK¢Q0ußL¨KWh¨ßMÉßNËßOËßP¨num-rgu¨´x´É´y´ÑßRÉßS»}{ßBßCßD¨光伏电压¨ßF¨fad fa-solar-panel¨ßH¨#076EEF¨ßJÉßKº0ßL´V´ßMÉßNÍßOËßP¨num-m2k¨´x´É´y´ÍßRÊßS»}{ßBßCßD¨电池电量¨ßFßGßHßaßJÉßKº0ßL´%´ßMÉßNÍßOËßP¨num-ahb¨´x´Í´y´ËßRËßS»}{ßBßCßD¨温度¨ßF¨fad fa-thermometer-three-quarters¨ßH¨#EA0909¨ßJÉßKº0ßL´℃´ßMÉßNËßOËßP¨num-ycz¨´x´Í´y´ÑßS»ßRÉ}{ßBßCßD¨累计电费¨ßF¨fad fa-handshake-alt¨ßH¨#6010E4¨ßJÉßK¢Q0tßL¨CNY¨ßMÉßNËßOËßP¨num-ao5¨´x´Ë´y´ÑßRÉßS»}{ßBßCßD¨充电电流¨ßF¨fad fa-tachometer-alt-fast¨ßHßgßJÉßKº0ßL´A´ßMÉßNÍßOËßP¨num-3bm¨´x´Í´y´ÏßRÊßS»}{ßBßCßD¨电池电压¨ßF¨fad fa-database¨ßHßaßJÉßKº0ßL´V´ßMÉßNÍßOËßP¨num-l24¨´x´Í´y´ÍßRÊßS»}{ßBßCßD¨光伏电流¨ßFßoßHßgßJÉßKº0ßL´A´ßMÉßNÍßOËßP¨num-5up¨´x´É´y´ÏßRÊßS»}{ßB¨cha¨ßMɨsty¨¨line¨ßH¨#389BEE¨¨sty1¨ßx¨clr1¨ßy¨sty2¨ßx¨clr2¨ßyßNÑßOÌßP¨cha-9ay¨´x´É´y´¤A¨key0¨¨data-1¨¨key1¨¨data-2¨¨key2¨¨data-3¨ßD¨光伏功率¨´t1´´电压´´t2´´电流´}{ßB¨deb¨¨mode¨ÉßMÉßNÑßOÌßP¨debug¨´x´É´y´¤H}{ßB¨btn¨ßF¨fad fa-charging-station¨ß1CÉßD¨充电中¨´t1´¨文本2¨ßMÉßNÊßOÊßP¨btn-e6n¨´x´Ï´y´ÑßRÍ}{ßBß1EßF¨fad fa-sun¨ß1CÉßD¨文本1¨´t1´ß1HßMÉßNÊßOÊßP¨btn-e3t¨´x´Ð´y´ÑßRÍ}{ßBß1EßF¨fad fa-signal¨ß1CÉßDß1K´t1´ß1HßMÉßNÊßOÊßP¨btn-q4l¨´x´Ð´y´ÒßRÍ}{ßBß1EßF¨fad fa-signal-1¨ß1CÉßDß1K´t1´ß1HßMÉßNÊßOÊßP¨btn-ogw¨´x´Ï´y´ÒßRÍ}{ßBßvßMÉßwßxßHßyßzßxß10ßyß11ßxß12ßyßNÑßOÌßP¨cha-nud¨´x´É´y´¤Dß14¨data-4¨ßD¨充电功率¨ß16¨data-5¨´t1´´电压´ß18¨data-6¨´t2´´电流´}÷¨actions¨|÷¨triggers¨|÷ßS|ßQßXßbßdßhßmßpßsßu÷}
|
Loading…
Reference in New Issue