import time
from machine import SoftI2C, Pin, ADC
from machine_i2c_lcd import I2cLcd
time.sleep(0.1) # Wait for USB to become ready
# LCD Setup
LCDAddress = 0x27
i2c = SoftI2C(scl=Pin(21), sda=Pin(20), freq=400000) # connect scl to GPIO 21, sda to GPIO 20
lcd = I2cLcd(i2c, LCDAddress, 4, 20)
SelectPin = Pin(2, Pin.IN, Pin.PULL_UP)
ScrollPin = Pin(3, Pin.IN, Pin.PULL_UP)
BackPin = Pin(4, Pin.IN, Pin.PULL_UP)
Ohm1 = Pin(10, Pin.IN, Pin.PULL_UP)
Ohm2 = Pin(11, Pin.IN, Pin.PULL_UP)
Ohm3 = Pin(12, Pin.IN, Pin.PULL_UP)
Ohm4 = Pin(13, Pin.IN, Pin.PULL_UP)
CapMeterPin = Pin(15, Pin.IN)
Volt1 = Pin(16, Pin.IN, Pin.PULL_UP)
Volt2 = Pin(17, Pin.IN, Pin.PULL_UP)
Volt3 = Pin(18, Pin.IN, Pin.PULL_UP)
Volt4 = Pin(19, Pin.IN, Pin.PULL_UP)
AmmeterPin = ADC(28)
VoltmeterPin = ADC(27)
OhmmeterPin = ADC(26)
VoltageReference = 3.3 # Voltage of Pico
ADCResolution = 65535 # ADC range of Pico
# Capacitance Meter Setup
RA = 1000
RB_VALUES = [100000, 10000, 1000]
# Voltmeter Setup
# Scaling factors
RANGES = {
"3.3V": 1.0,
"10V": 3.2,
"25V": 8.5,
"50V": 17.0
}
# Menu Setup
MenuOptions = {
1: "Ammeter ",
2: "Capacitance Meter",
3: "Voltmeter ",
4: "Ohmmeter "
}
# Ammeter Functions
def Get_Current():
ShuntResistor = 0.5
RawCurrent = AmmeterPin.read_u16()
voltageAcrossShunt = (RawCurrent * VoltageReference) / ADCResolution;
Current = voltageAcrossShunt / ShuntResistor
return Current
def Display_Current(Current):
StrCurrent = "{:.2f}A".format(Current)
lcd.move_to(0,0)
lcd.putstr("Current:")
lcd.move_to(0,1)
lcd.putstr(StrCurrent)
def Ammeter():
Current = Get_Current()
Display_Current(Current)
# Capacitance Meter Functions
def measure_frequency(duration=1):
count = 0
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < duration * 1000:
if CapMeterPin.value() == 1:
while CapMeterPin.value() == 1:
pass
count += 1
return count / duration
# Calculate capacitance
def calc_C(f, RB):
if f == 0:
return 0
return 1 / (0.693 * (RA + 2 * RB) * f)
# Auto-range RB
def select_RB(f):
if f > 2000:
return 100000
elif f > 100:
return 10000
else:
return 1000
# Format capacitance
def format_C(C):
if C >= 1e-6:
return "{:.2f}uF".format(C * 1e6)
elif C >= 1e-9:
return "{:.2f}nF".format(C * 1e9)
else:
return "{:.2f}pF".format(C * 1e12)
# Format frequency
def format_f(f):
if f >= 1000:
return "{:.1f}kHz".format(f / 1000)
else:
return "{:.0f}Hz".format(f)
def CapMeter():
f = measure_frequency()
RB = select_RB(f)
C = calc_C(f, RB)
lcd.move_to(0, 0)
lcd.putstr("F:" + format_f(f))
lcd.move_to(0, 1)
lcd.putstr("C:" + format_C(C))
time.sleep(1)
# Voltmeter Functions
def Voltmeter_read_voltage():
raw = VoltmeterPin.read_u16()
return (raw / ADCResolution) * VoltageReference
def get_range():
if not Volt1.value():
return "3.3V"
elif not Volt2.value():
return "10V"
elif not Volt3.value():
return "25V"
elif not Volt4.value():
return "50V"
else:
return None # nothing selected (bad wiring)
def Display_Voltage(Range, v_actual):
lcd.move_to(0,0)
lcd.putstr("Voltage")
lcd.move_to(0,1)
lcd.putstr("Range: " + str(Range) + " ")
lcd.move_to(0,2)
lcd.putstr("Vout: " + str(v_actual) + " ")
def Display_Voltage_No_Range():
lcd.move_to(0,0)
lcd.putstr("Voltage")
lcd.move_to(0,1)
lcd.putstr("No range selected ")
lcd.move_to(0,2)
lcd.putstr(" ")
def Measure_Voltage():
current_range = get_range()
if current_range is None:
Display_Voltage_No_Range()
time.sleep(0.5)
return
v_out = Voltmeter_read_voltage()
v_actual = v_out * RANGES[current_range] # Final Voltage Measurement
Display_Voltage(current_range, v_out)
time.sleep(0.5)
def Voltmeter():
Measure_Voltage()
time.sleep_ms(200)
# Ohmmeter Functions
def Ohmmeter_read_voltage():
raw = OhmmeterPin.read_u16()
return (raw / ADCResolution) * VoltageReference
def get_r_known():
if not Ohm1.value():
return "100 Ohms" # 0–100Ω range
elif not Ohm2.value():
return "1k Ohms" # 100Ω–1kΩ
elif not Ohm3.value():
return "10k Ohms" # 1kΩ–10kΩ
elif not Ohm4.value():
return "100k Ohms" # 10kΩ–100kΩ
else:
return None
def measure_resistance():
r_known = get_r_known()
if r_known is None:
return None, None
v_out = Ohmmeter_read_voltage()
# invalid region checks (avoid divide errors / saturation)
if v_out <= 0.001 or v_out >= (VREF - 0.001):
return r_known, None
r_unknown = r_known * (v_out / (VREF - v_out))
return r_known, r_unknown
def Ohmmeter_display(r_known, r_unknown):
lcd.clear()
# Line 1: Resistance
lcd.move_to(0, 0)
if r_unknown is None:
lcd.putstr("Resistance:")
else:
lcd.putstr("Resistance: " + str(int(r_unknown)))
# Line 2: Range
lcd.move_to(0, 1)
if r_known is None:
lcd.putstr("Range: NONE")
else:
lcd.putstr("Range: " + r_known)
def Ohmmeter():
r_known, r_unknown = measure_resistance()
Ohmmeter_display(r_known, r_unknown)
time.sleep(3)
# Menu Function
def Display_Menu(Selected):
Selected = MenuOptions[Selected]
lcd.move_to(0,0)
lcd.putstr("Menu:")
lcd.move_to(0,1)
lcd.putstr(Selected + " ")
def Menu():
Selected = 1
while True:
Display_Menu(Selected)
if ScrollPin.value() != 1:
Selected += 1
if Selected == 5:
Selected = 1
time.sleep(0.2)
elif SelectPin.value() != 1:
return Selected
time.sleep(0.1)
def Main():
while True:
time.sleep(1)
lcd.clear()
Selected = Menu()
lcd.clear()
if Selected == 1:
while BackPin.value() == 1:
Ammeter()
time.sleep(0.1)
elif Selected == 2:
while BackPin.value() == 1:
CapMeter()
time.sleep(0.1)
elif Selected == 3:
while BackPin.value() == 1:
Voltmeter()
time.sleep(0.1)
elif Selected == 4:
while BackPin.value() == 1:
Ohmmeter()
time.sleep(0.1)
else:
print("Something's wrong")
Main()Cap-Meter
Ammeter
Voltmeter
Ohmmeter
Back
Scroll
Select