Share My Creation My New 12V Battery Charger with Three Functions

Hello everyone, how are you?
I want to share with you my newest 12V lead-acid battery charger with three functions: Fast Charge, Slow Charge, Floating Charge, and Stand-by.
Here is the code.
Best regards to all, see you later
B4R:
Sub Process_Globals
    Public Serial1 As Serial
    Public I2C_Erro As Byte
    Public Voltagem, Corrente, Shunt, Vdd, LoadVoltage As Float
    Public Pwm1 As Pin
    Public BtnUp As Pin
    Public BtnDown As Pin
    Public Pwm1 As Pin
    Public Led_Standby As Pin
    Public Led_Carregamento_Rapido As Pin
    Public Led_Crregamendo_Lento As Pin
    Public Led_Frutua As Pin
    Public Buzze As Pin
    ' Controle
    Public PWM_Value As Int = 0
    Public Timer1 As Timer
    Public Overflow As Boolean
    Public MODO As Byte
    Dim Limite_Voltage_BulkHigh As Float = 14.2
    Dim Limite_Voltage_FloatLow As Float = 13.8
    Dim PWM_Rapido As Int = 175     ' ajuste para carga rápida
    Dim PWM_Lento As Int = 157      ' ajuste para flutuação (~13.6-13.8V)
    Public Pisca As Boolean
    Private Stop As Timer
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    'Led de endicação
    Led_Carregamento_Rapido.Initialize(5,Led_Carregamento_Rapido.MODE_OUTPUT)
    Led_Crregamendo_Lento.Initialize(10,Led_Crregamendo_Lento.MODE_OUTPUT)
    Led_Standby.Initialize(12,Led_Standby.MODE_OUTPUT)
    Led_Frutua.Initialize(13,Led_Frutua.MODE_OUTPUT)
    Buzze.Initialize(9,Buzze.MODE_OUTPUT)
    
    ' Botões
    BtnUp.Initialize(14, BtnUp.MODE_INPUT_PULLUP)
    BtnUp.AddListener("PWM_MA")
    BtnDown.Initialize(15, BtnDown.MODE_INPUT_PULLUP)
    BtnDown.AddListener("PWM_ME")
    ' Saída PWM
    Pwm1.Initialize(11,Pwm1.MODE_OUTPUT)
    ' Configura PWM inicial
    Pwm1.AnalogWrite(PWM_Value)
    Timer1.Initialize("Controle", 500)
    Timer1.Enabled = True
    Stop.Initialize("Buzzer",100)
    Stop.Enabled = True
    MODO = 0
    RunNative("Init", Null)
End Sub

Sub Buzzer
    Buzze.DigitalWrite(True)
    Delay(200)
    Buzze.DigitalWrite(False)
    Stop.Enabled = False ' Desativa o timer após 3 apitos
End Sub

Sub PWM_MA(State As Boolean)
        If State = False And BtnDown.DigitalRead = True Then
        MODO = (MODO + 1) Mod 4   ' roda de 0 até 4 e volta para 0
        Stop.Enabled = True       ' Desativa o timer após 3 apitos
    End If
End Sub
Sub PWM_ME(State As Boolean)
    If BtnDown.DigitalRead = False Then
       PWM_Value = PWM_Value + 1
    End If
End Sub
Sub Controle
    RunNative("Read", Null)    ' atualiza Voltagem, Corrente etc. preenchidos na parte C
    If I2C_Erro <> 0 Then
        Log("Erro I2C: ", I2C_Erro)
        Timer1.Enabled = False
        Return
    End If
    
    Select MODO
        Case 0   '
            Log("⏹️ Estado: Standby")
            Pwm1.AnalogWrite(PWM_Value)   ' Força 0 no Standby
            Led_Carregamento_Rapido.DigitalWrite(False)
            Led_Crregamendo_Lento.DigitalWrite(False)
            Led_Standby.DigitalWrite(True)
            Led_Frutua.DigitalWrite(False)

        Case 1
            Log("⚡ Carga Rabida")
            Pwm1.AnalogWrite(PWM_Rapido)
            Pisca = Not(Pisca)
            Led_Carregamento_Rapido.DigitalWrite(Pisca)
            Led_Crregamendo_Lento.DigitalWrite(False)
            Led_Standby.DigitalWrite(False)
            Led_Frutua.DigitalWrite(False)
    
            If Corrente > 15 Then
            If Voltagem >= Limite_Voltage_BulkHigh Then
                Log("⚡ Atingiu 14.2V - Limitando Corrente")
                PWM_Value = PWM_Rapido
            End If
            Else
                Log("⚠️ Nenhuma bateria conectada")
            End If
        
        Case 2   
            Log("️⚡ Carga Lenta")
            Pwm1.AnalogWrite(PWM_Lento)
            Pisca = Not(Pisca)
            Led_Crregamendo_Lento.DigitalWrite(Pisca)
            Led_Carregamento_Rapido.DigitalWrite(False)
            Led_Standby.DigitalWrite(False)
            Led_Frutua.DigitalWrite(False)
        
            If Corrente > 15 Then
            If Voltagem >= Limite_Voltage_BulkHigh Then
                Log("🔄 Atingiu 14.2V - Limitando Corrente")
                PWM_Value = PWM_Lento
            End If
            Else
                Log("⚠️ Nenhuma bateria conectada")
            End If
        
        Case 3
            Log("⏹️ Carga Lenta + Flutuação")
            Pwm1.AnalogWrite(PWM_Lento)
            Pisca = Not(Pisca)
            Led_Carregamento_Rapido.DigitalWrite(False)
            Led_Crregamendo_Lento.DigitalWrite(False)
            Led_Standby.DigitalWrite(False)
            Led_Frutua.DigitalWrite(Pisca)
            
            If Corrente > 15 Then
            If Voltagem <= Limite_Voltage_FloatLow Then
                Log("⬇️ Bateria Caiu Abaixo de 12.5V - voltando para Carga Rápida")
                MODO = 1
            End If
            Else
                Log("⚠️ Nenhuma bateria conectada")
            End If
    End Select
    
    Log("| Vbat = ", NumberFormat(Voltagem,1,1), " V | I = ", NumberFormat(Corrente,1,0), " mA = | A = ",NumberFormat(Corrente/1000.0,1,2)," | PWM = ",NumberFormat(PWM_Value,1,1))
    If Overflow Then
        Log("⚠️ Overflow detectado!")
    Else
        Log("✓ Leitura OK")
    End If
End Sub
#If C
#include <Wire.h>
#include <INA226_WE.h>
INA226_WE ina226 = INA226_WE(0x40);

  void Init(B4R::Object* o) {
  Wire.begin(); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
 // Wire.begin(18, 19); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
  bool status = ina226.init();
  if (!status) {
  b4r_main::_i2c_erro = 99; // erro de inicialização
  return;
  }
  ina226.setAverage(AVERAGE_64);
  ina226.setConversionTime(CONV_TIME_1100, CONV_TIME_1100);
  // Calibração para resistor shunt R01F = 0.01 Ω
  //ina226.setResistorRange(0.1, 2.0);
  ina226.setResistorRange(0.01, 10.0); 
  ina226.waitUntilConversionCompleted(); // evitar leitura zero na 1ª medição
  b4r_main::_i2c_erro = 0;
}
  void Read(B4R::Object* o) {
  byte errorCode = ina226.getI2cErrorCode();
  b4r_main::_i2c_erro = errorCode;
  if (errorCode != 0) return;
  float bus = ina226.getBusVoltage_V();
  float shunt_mV = ina226.getShuntVoltage_mV();
  //float current_mA = ina226.getCurrent_mA();
  float current_mA = ina226.getCurrent_mA() * 0.897; // fator de calibração ajustado
  float power_mW = ina226.getBusPower();
 
  b4r_main::_voltagem = bus;
  b4r_main::_shunt = shunt_mV;
  b4r_main::_corrente = current_mA;
  b4r_main::_vdd = power_mW;
  b4r_main::_loadvoltage = bus + (shunt_mV / 1000.0);
  b4r_main::_overflow = ina226.overflow;
}
#End If
 

Attachments

  • Imagem do WhatsApp de 2025-09-25 à(s) 17.08.38_734fe1c1.jpg
    Imagem do WhatsApp de 2025-09-25 à(s) 17.08.38_734fe1c1.jpg
    178.3 KB · Views: 17
  • Imagem do WhatsApp de 2025-09-25 à(s) 17.08.38_c05ac4dd.jpg
    Imagem do WhatsApp de 2025-09-25 à(s) 17.08.38_c05ac4dd.jpg
    145.3 KB · Views: 18

emexes

Expert
Licensed User
Longtime User
I like both the B4R heart and the debugging scorch mark.

I'm sure there is a story behind each.
 

emexes

Expert
Licensed User
Longtime User
First draft quick response from B4X nightshift volunteer 🍻 :

Why would anyone want a "slow charge"?

Extend life of battery, would be my first guess.

Also maybe to charge smaller batteries, without causing overheating due to too high a current relative to battery capacity.
 
Top