Skip to content

Instantly share code, notes, and snippets.

@Szpadel
Last active July 4, 2023 12:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Szpadel/9386d4eef06bb75c050db48175c104ba to your computer and use it in GitHub Desktop.
Save Szpadel/9386d4eef06bb75c050db48175c104ba to your computer and use it in GitHub Desktop.
Resistor Divider CALCulator
#!/usr/bin/env python3
import argparse
import itertools
from rich import print
from rich.table import Table
def format_resistor_value(number):
formatted_number = format(number, ".1f").rstrip('0').rstrip('.')
return formatted_number
def parse_resistor_values(resistor_values):
parsed_values = []
for value in resistor_values:
if 'K' in value:
parsed_values.append(float(value.replace('K', '')) * 1000)
elif 'M' in value:
parsed_values.append(float(value.replace('M', '')) * 1000000)
else:
parsed_values.append(float(value))
return parsed_values
def format_resistor_values(resistor_values):
formatted_values = []
for value in resistor_values:
if value >= 1000000:
formatted_values.append(format_resistor_value(value/1000000) + 'M')
elif value >= 1000:
formatted_values.append(format_resistor_value(value/1000) + 'K')
else:
formatted_values.append(format_resistor_value(value))
return ', '.join(formatted_values)
def parallel_resistor(*resistors):
return 1 / sum(1/r for r in resistors)
def series_resistor(*resistors):
return sum(resistors)
def voltage_divider(vin, r1, r2):
return vin * (r2 / (r1 + r2))
def find_combinations_within_tolerance(vin, vout, resistors, resistor_count, voltage_tolerance):
combinations = []
for r in range(1, resistor_count+1):
combinations.extend(list(itertools.combinations_with_replacement(resistors, r)))
valid_output_values = []
for comb in combinations:
# try R1 in series and R2 in series and parallel
r1_s = series_resistor(*comb)
for r_comb in combinations:
r2_s = series_resistor(*r_comb)
r2_p = parallel_resistor(*r_comb)
vout_s = voltage_divider(vin, r1_s, r2_s)
vout_p = voltage_divider(vin, r1_s, r2_p)
if abs((vout - vout_s) / vout) <= voltage_tolerance:
valid_output_values.append((comb, r_comb, vout_s, 'series', 'series', vout - vout_s, r1_s, r2_s))
if abs((vout - vout_p) / vout) <= voltage_tolerance:
valid_output_values.append((comb, r_comb, vout_p, 'series', 'parallel', vout - vout_p, r1_s, r2_p))
# try R1 in parallel and R2 in series and parallel
r1_p = parallel_resistor(*comb)
for r_comb in combinations:
r2_s = series_resistor(*r_comb)
r2_p = parallel_resistor(*r_comb)
vout_s = voltage_divider(vin, r1_p, r2_s)
vout_p = voltage_divider(vin, r1_p, r2_p)
if abs((vout - vout_s) / vout) <= voltage_tolerance:
valid_output_values.append((comb, r_comb, vout_s, 'parallel', 'series', vout - vout_s, r1_p, r2_s))
if abs((vout - vout_p) / vout) <= voltage_tolerance:
valid_output_values.append((comb, r_comb, vout_p, 'parallel', 'parallel', vout - vout_p, r1_p, r2_p))
return valid_output_values
def main():
parser = argparse.ArgumentParser(description='Find the closest voltage divider resistor combinations')
parser.add_argument('--vin', type=float, required=True, help='input voltage in volts')
parser.add_argument('--vout', type=float, required=True, help='desired output voltage in volts')
parser.add_argument('--resistor_values', nargs='+', required=True, help='resistor values in ohms. Format: 10 1K 2M etc.')
parser.add_argument('--resistor_count', type=int, default=2, help='number of resistors that can be used')
parser.add_argument('--num_results', type=int, default=5, help='number of best results')
parser.add_argument('--voltage_tolerance', type=float, default=0.05, help='minimum voltage tolerance in volts')
args = parser.parse_args()
parsed_resistor_values = parse_resistor_values(args.resistor_values)
combinations = find_combinations_within_tolerance(args.vin, args.vout, parsed_resistor_values, args.resistor_count, args.voltage_tolerance)
combinations.sort(key=lambda x: (len(x[0]) + len(x[1]), abs(x[5]))) # sort by least resistors used and absolute voltage difference
print(f"[bold cyan]Searching for voltage divider resistor combinations for the following parameters:[/bold cyan]")
print(f"Input Voltage (Vin): [bold magenta]{args.vin}V[/bold magenta]")
print(f"Desired Output Voltage (Vout): [bold magenta]{args.vout}V[/bold magenta]")
print(f"Voltage Tolerance: [bold magenta]{args.voltage_tolerance*100}%[/bold magenta]")
print(f"Resistor Values: [bold magenta]{format_resistor_values(parsed_resistor_values)} Ohms[/bold magenta]")
print(f"Maximum Number of Resistors: [bold magenta]{args.resistor_count}[/bold magenta]\n")
print("""Vin ───[ R1 ]───╮
├─── Vout
GND ───[ R2 ]───╯
""")
table = Table(show_header=True, header_style="bold magenta")
table.add_column("Individual resistors for R1", style="dim")
table.add_column("R1 Configuration", style="dim")
table.add_column("Final R1 (Ohms)", style="dim")
table.add_column("Individual resistors for R2", style="dim")
table.add_column("R2 Configuration", style="dim")
table.add_column("Final R2 (Ohms)", style="dim")
table.add_column("Vout (Volts)", style="dim")
table.add_column("Voltage Difference to Target (Volts)", style="dim")
for comb in combinations[:args.num_results]:
table.add_row(format_resistor_values(comb[0]), comb[3], format_resistor_values([comb[6]]), format_resistor_values(comb[1]), comb[4], format_resistor_values([comb[7]]), "{:.4f}".format(comb[2]), "{:.4f}".format(comb[5]))
print(table)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment