Share My Creation Digital Adjustable Power Supply

Digital Adjustable Power Supply

This project was developed using an Arduino Nano, providing precise output voltage control from 0 to 25V, with adjustment in both volts and millivolts through a rotary encoder.

Project Features​

  • Adjustable output voltage from 0 to 25V
  • Fine adjustment in volts and millivolts
  • I2C LCD display for real-time information
  • Rotary encoder control
  • Temperature monitoring using an NTC sensor
  • Automatic cooling fan control for transistor temperature management
  • Short-circuit protection using transistor switching at the LM358 input
  • Audible buzzer alarm for warnings and protection
  • Real-time voltage and current monitoring

Tests Performed​

A continuous test was conducted for approximately 30 minutes using an automotive lamp as the load, drawing about 1.7A. During the test, the system operated reliably and maintained stable voltage regulation while continuously monitoring the temperature of the power components.

Applications​

Ideal for electronics workbenches, equipment maintenance, circuit testing, and electronic project development.

B4R:
Sub Process_Globals
    Public Serial1 As Serial
    Public Serial1 As Serial
    Public Timer_ADC As Timer
    Public TimerControle As Timer

    ' -------- PINOS --------
    Public Volt_Leitura As Pin  ' A0 (pino 14) - tensão
    Public Ampe_Leitura As Pin  ' A1 (pino 15) - corrente ACS712 30A
    Public Pwm1 As Pin          ' D11 - saída PWM
    Public Buzzer As Pin        ' D8
    Public Modo As Pin          ' D9  - botão confirmar
    Public Pwm_Mais As Pin      ' A2 (pino 16) - ajuste fino +1
    Public Pwm_Menos As Pin     ' A3 (pino 17) - ajuste fino -1
    Public EncoderPinA As Pin   ' D12
    Public EncoderPinB As Pin   ' D10
    Public Ventilador As Pin    ' D4
    Public Prot As Pin      ' D3
   
    ' -------- VALORES MEDIDOS --------
    Public BatVolt As Float
    Public Amper As Float
    Public PWM_Value As Int = 4
    Public Amper2 As Float
    Public Amper_Valor As Float
   
   
    ' -------- CALIBRAÇÃO CORRENTE --------
    Public OffsetAmp As Float
    Public OffsetAmp2 As Float
    Public OffsetAmpCalibrado As Boolean = False

    Public LastStateTime As Long
    Public LastMaisTime As Long
    Public LastMenosTime As Long

    ' -------- LCD --------
    Public lcd As LiquidCrystal_I2C
    Public bc As ByteConverter
   
    ' ----- Leitura ntc -----
    Public R_NTC As Float
    Public lnR As Float
    Public invT As Float
    Public tempK As Float
    Public tempC As Float
    Public Vref As Double = 4.8        ' Referência de tensão do ADC (3.3V se for NodeMCU/Wemos)
    Public VentLigado As Boolean
    Public Ntc_Leitura As Pin
    Public beta As Float
    Public leitura As Int
    Public tensao As Float
    Public R0 As Float
    Public T0 As Float
   
    Public Modo_Volte As Int
    Public Modo_Set As Byte
    Public Modo_Set2 As Boolean
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    Modo.Initialize(9, Modo.MODE_INPUT_PULLUP)'D9
    Modo.AddListener("Menu_Modo")
   
    Buzzer.Initialize(8, Buzzer.MODE_OUTPUT)  'D8
    Pwm1.Initialize(11, Pwm1.MODE_OUTPUT)     'D11
    Pwm1.AnalogWrite(0)
   
    Volt_Leitura.Initialize(14, Volt_Leitura.MODE_INPUT)     ' A0
    Ampe_Leitura.Initialize(15, Ampe_Leitura.MODE_INPUT)     ' A1
    Ntc_Leitura.Initialize(21, Ntc_Leitura.MODE_INPUT)       ' D7
   
    Pwm_Mais.Initialize(16, Pwm_Mais.MODE_INPUT_PULLUP)      ' A2
    Pwm_Menos.Initialize(17, Pwm_Menos.MODE_INPUT_PULLUP)    ' A3

    EncoderPinA.Initialize(12, EncoderPinA.MODE_INPUT_PULLUP)' D12
    EncoderPinB.Initialize(10, EncoderPinB.MODE_INPUT_PULLUP)' D10
    EncoderPinA.AddListener("EncoderPinA_Changed")
   
    Prot.Initialize(3, Prot.MODE_OUTPUT)                     ' D3
    Prot.DigitalWrite(False)
   
    Ventilador.Initialize(4, Ventilador.MODE_OUTPUT)         ' D4
    Ventilador.DigitalWrite(False)
   
    Timer_ADC.Initialize("Read_ADC", 500)
    Timer_ADC.Enabled = True
   
    TimerControle.Initialize("Controle", 200)
    TimerControle.Enabled = True

    lcd.Initialize(0x27, 16, 2)
    lcd.Backlight = True
    lcd.Clear
    lcd.SetCursor(0, 0)
    lcd.Write("FONTE DE BANCADA")
    lcd.SetCursor(0, 1)
    lcd.Write("  Iniciando...  ")
    Delay(1500)
    lcd.Clear
   
    lcd.SetCursor(7, 0)
    lcd.Write("V  ")
    lcd.SetCursor(10, 1)
    lcd.Write(">")
    Modo_Set = 0
End Sub

Sub Menu_Modo(State As Boolean)

    If State = False Then
        If Millis - LastMaisTime > 200 Then
        LastMaisTime = Millis
        Bipe2
        Modo_Set = Modo_Set + 1
        If Modo_Set > 2 Then Modo_Set = 0
        End If
    End If
   
    Select Modo_Set
        Case 0
            lcd.SetCursor(10, 1)
            lcd.Write(">")
            lcd.SetCursor(7, 1)
            lcd.Write(" ")
           
            lcd.SetCursor(7, 0)
            lcd.Write("V  ")
        Case 1
            lcd.SetCursor(7, 0)
            lcd.Write("mV ")
        Case 2
            lcd.SetCursor(7, 1)
            lcd.Write("<")
         
            lcd.SetCursor(10, 1)
            lcd.Write(" ")
    End Select
   
End Sub

' =============================================================
' ENCODER
' =============================================================
Sub EncoderPinA_Changed(State As Boolean)
    Dim t As Long = Millis
    If t - LastStateTime < 150 Then Return
    LastStateTime = t
   
    Dim Incremento As Float
    If EncoderPinB.DigitalRead Then
        Incremento = 1
        Bipe2
    Else
        Incremento = -1
        Bipe2
    End If
   
    Select Modo_Set
                Case 0
                Modo_Volte = Modo_Volte + Incremento
            Select Modo_Volte
                Case 1
                    PWM_Value = 15
                Case 2
                    PWM_Value = 24
                Case 3
                    PWM_Value = 34
                Case 4
                    PWM_Value = 44
                Case 5
                    PWM_Value = 53
                Case 6
                    PWM_Value = 62
                Case 7
                    PWM_Value = 72
                Case 8
                    PWM_Value = 81
                Case 9
                    PWM_Value = 90
                Case 10
                    PWM_Value = 99
                Case 11
                    PWM_Value = 109
                Case 12
                    PWM_Value = 118
                Case 13
                    PWM_Value = 128
                Case 14
                    PWM_Value = 137
                Case 15
                    PWM_Value = 146
                Case 16
                    PWM_Value = 156
                Case 17
                    PWM_Value = 167
            End Select
        Case 1
            PWM_Value = PWM_Value + Incremento
        Case 2
            Amper_Valor = Amper_Valor + (Incremento * 0.01)
            If Amper_Valor < 0 Then Amper_Valor = 0
            If Amper_Valor > 5 Then Amper_Valor = 5
End Select
End Sub

' =============================================================
' LEITURA ADC (timer 500ms)
' =============================================================
Sub Read_ADC
    '-------------Leitura da Voltagem 0 25v ---------------
    ' --- TENSÃO — R1=22k, R2=2k2, fator=10.57 ---
    Dim rawBat As UInt = Volt_Leitura.AnalogRead
    Dim vA0 As Float = rawBat * (5.0 / 1023.0)
    BatVolt = vA0 * 10.50

    '-------------Leitura da Amperagem ate 5 Amper ---------
    ' --- CORRENTE — ACS712 30A (66mV/A) ---
    If OffsetAmpCalibrado = False Then
    Dim SomaOffset As Float = 0
    For i = 1 To 100
    SomaOffset = SomaOffset + Ampe_Leitura.AnalogRead
    Next
    OffsetAmp = (SomaOffset / 100) * (5.0 / 1023.0)
    OffsetAmpCalibrado = True
    Amper = 0
        Return
    End If
    Dim Soma As Float = 0
    For i = 1 To 20
        Soma = Soma + Ampe_Leitura.AnalogRead
    Next
    Dim rawAmp As Float = Soma / 20
    Dim vAmp As Float = rawAmp * (5.0 / 1023.0)
    Amper = Abs((vAmp - OffsetAmp) / 0.066)
    If Amper < 0.10 Then Amper = 0
   
    '-------------Leitura do ntc---------------
    ' ----- Parâmetros -----
    beta = 3950        ' Constante Beta do NTC (ajuste fino depois)
    R0 = 10000         ' Resistor fixo (10k)
    T0 = 298.15        ' 25 °C em Kelvin
    Vref = 3.3         ' ATENÇÃO: use 1.0 se seu A0 for de módulo “puro” (TOUT = 1.0V máx)

    ' ----- Leitura -----
    leitura = Ntc_Leitura.AnalogRead      ' 0..1023
    tensao = leitura * Vref / 1023.0

    ' ----- Resistência do NTC (resistor no GND, NTC no +V) -----
    ' Fórmula correta para este esquema:
    ' R_NTC = R0 * (Vref - tensao) / tensao
    If tensao <= 0 Then tensao = 0.001      ' evita divisão por zero
    If tensao >= Vref Then tensao = Vref - 0.001

    R_NTC = R0 * (Vref - tensao) / tensao

    ' ----- Temperatura (Lei Beta) -----
    lnR = Logarithm(R_NTC / R0, 2.718281828)
    invT = (1 / T0) + (1 / beta) * lnR
    tempK = 1 / invT
    tempC = tempK - 273.15

    ' ----- Calibração opcional -----
'    correcao = 4.8
'    tempC = tempC + correcao

    ' ----- Controle do ventilador -----
    If tempC > 40 And VentLigado = False Then
        Ventilador.DigitalWrite(True)
        VentLigado = True
    Else If tempC < 35 And VentLigado = True Then
        Ventilador.DigitalWrite(False)
        VentLigado = False
    End If
   
   
    Dim Grau() As Byte = Array As Byte(223)
    Log("Temperatura: ", NumberFormat(tempC, 1,0)," °C")
'    ' Linha 0: atualiza tensão
    lcd.SetCursor(0, 0)
    lcd.Write(NumberFormat(tempC, 2, 1))
    lcd.Write(Grau)
    lcd.Write("C")
   

'------------------Amperimetro---------------------------
    Dim texto As String
    If Amper < 1 Then
        texto = NumberFormat(Amper * 1000, 1, 0)
    Else
        texto = NumberFormat(Amper, 1, 2)
    End If
    Dim textoBytes() As Byte = texto.GetBytes
    Dim Pos As Int = bc.IndexOf(textoBytes, ".".GetBytes)
    If Pos <> -1 Then
        textoBytes(Pos) = ",".GetBytes(0)
    End If
    lcd.SetCursor(11, 1)
    lcd.Write(bc.StringFromBytes(textoBytes))
    If Amper < 1 Then
        lcd.Write("mA ")
    Else
        lcd.Write("A  ")
    End If
   
   
    '------------------ Limite de Amperímetro ---------------------------
    Dim texto2 As String = NumberFormat(Amper_Valor, 1, 2)
    Dim textoBytes2() As Byte = texto2.GetBytes
    Dim Pos2 As Int = bc.IndexOf(textoBytes2, ".".GetBytes)

    If Pos2 <> -1 Then
        textoBytes2(Pos2) = ",".GetBytes(0)
    End If

    lcd.SetCursor(0, 1)
    lcd.Write(bc.StringFromBytes(textoBytes2))
   
    If Amper_Valor > 1 Then
        lcd.Write("A  ")
    Else
        lcd.Write("mA ")
    End If
   
    ' Linha 0: atualiza tensão
    lcd.SetCursor(10, 0)
    lcd.Write(">")
   

   
   
   
'------------------Voltimetro---------------------------
    Dim texto As String = NumberFormat(BatVolt, 2, 1)
    Dim textoBytes() As Byte = texto.GetBytes
    Dim Pos As Int = bc.IndexOf(textoBytes, ".".GetBytes)
    If Pos <> -1 Then
        textoBytes(Pos) = ",".GetBytes(0)
    End If
    lcd.SetCursor(11, 0)
    lcd.Write(bc.StringFromBytes(textoBytes))
    lcd.Write("V")

End Sub


' =============================================================
' CONTROLE (timer 200ms)
' =============================================================
Sub Controle
    ' --- Botão ajuste fino + ---
    If Pwm_Mais.DigitalRead = False Then
        If Millis - LastMaisTime > 200 Then
            LastMaisTime = Millis
            PWM_Value = PWM_Value + 1
            Bipe2
            If PWM_Value > 255 Then PWM_Value = 255
        End If
    End If

    ' --- Botão ajuste fino - ---
    If Pwm_Menos.DigitalRead = False Then
        If Millis - LastMenosTime > 200 Then
            LastMenosTime = Millis
            PWM_Value = PWM_Value - 1
            Bipe2
            If PWM_Value < 0 Then PWM_Value = 0
        End If
    End If
   
    Pwm1.AnalogWrite(PWM_Value)
    Log("VALOR PWM: ", PWM_Value)

End Sub

Sub Bipe2
    Buzzer.DigitalWrite(True)
    Delay(20)
    Buzzer.DigitalWrite(False)
End Sub

 

Attachments

  • PCB.JPG
    PCB.JPG
    271 KB · Views: 15
Last edited:

Cableguy

Expert
Licensed User
Longtime User
Hi Cesar,

Great project you got here...
But be ware, your system automatically translatted the code to Portuguese, rendering it unusable for someone who does not understand the language.
 
Top