Ohm’s Helper: Scripting My Calculations
So, one day I found myself hunting for my calculator. I had been using the built-in calculator program on my laptop to calculate resistor requirements for LEDs, but I found myself craving the fancy formatting of my fancy graphing calculator. After digging through my backpack and office, I thought to myself “Aren’t you a programmer? Clearly it’s time for a script!”
Oh yes, there will be scripts…
As I opened up gedit and began coding some boilerplate, I thought about what my script should do. There are two main calculations that I find myself doing lately:
My script will automate these calculations. I decided that I’d use the pretty menu input menu over command line switches. Switches are supposed to save you time, but I find that unless I’m using some utility all the time I always end up doing foo -? first. I decided to use Lua for my script; I like Lua’s simple syntax, multiple return values, and first class functions.
Full source code can be found on my github. Arduino Helper is licensed under the GPLv3.
Functions in my script return two values: a value, and a user-friendly message. If some error is encountered, the value is nil, and the message is the error. If all is well, a number is returned, and an output message to the effect of “You got 3 foos! Huzzah!” is displayed. This message is printed at the end, and the value is largely unused at this time. I figure as I expand the script, a value will be nice to have to pass around to other functions as I add them.
Four functions make up the Ohm’s Law calculations:
local function res(i, v) return v / i, "V / I = " .. v / i .. " Ohms" end local function amp(r, v) if r == 0 then return nil, "R must be greater than 0!" end return v / r, "V / R = " .. v / r .. " Amps" end local function vol(r, i) if r == 0 then return nil, "R must be greater than 0!" end return r * i, "R * I = " .. r * i .. " Volts" end local function ohms_law(i, r, v) if i == nil then return amp(r, v) elseif r == nil then return res(i, v) elseif v == nil then return vol(r, i) else return nil, "i, r, or v must be nil!" end end
Res, amp, and vol are pretty self-explanatory, but ohms_law bears some explanation. Ohms_law takes 3 arguments: i, r, and v. It expects one of these to be nil. This is the value that it is solving for. Depending on which one is nil, it calls amp, res or vol.
LED Resistor Calculation
The LED calculation is handled by one function:
local function led_calc(supply, led_vol, led_curr, quantity) local v = (supply - (led_vol * quantity)) if v <= 0 then return nil, "Insufficient supply voltage!" end return res((led_curr / 1000), v) end
This function is also pretty straightforward: it takes the supply voltage, LED voltage, LED current, and quantity of LEDs, and applies Ohm’s Law to calculate the resistance (by calling res).
It does check to ensure that supply – (led_voltage – quantity) is greater than or equal to zero.
…and of course, we have our fancy menu. This is handled by one big function, with two nested functions:
local function director() local function gather_law() local r = nil local i = nil local v = nil print("\nSolve for:") print("1) R") print("2) I") print("3) V") local selection = io.read("*n") if selection ~= 1 and selection ~= 2 and selection ~= 3 then return nil, "Invalid Selection!" end if selection ~= 1 then print("Enter R in units of Ohms:") r = io.read("*n") end if selection ~= 2 then print("Enter I in units of Amps:") i = io.read("*n") end if selection ~= 3 then print("Enter V in units of Volts:") v = io.read("*n") end return ohms_law(i, r, v) end local function gather_led() local supply = nil local led_vol = nil local led_curr = nil local quantity = nil print("\nEnter the Supply Voltage:") supply = io.read("*n") print("Enter the LED Voltage:") led_vol = io.read("*n") print("Enter the LED Current in units of Miliamps:") led_curr = io.read("*n") print("Enter the amount of LEDs:") quantity = io.read("*n") return led_calc(supply, led_vol, led_curr, quantity) end print("Select your calculation:") print("1) Ohm's Law") print("2) LED Resistor Requirement") local selection = io.read("*n") if selection == 1 then return gather_law() elseif selection == 2 then return gather_led() else return nil, "Invalid Selection!" end end
This function handles all menu operations. I felt it would be good to keep calculations and menus separated into separate functions, so that if I want to reuse the calculation functions, I don’t need to worry about refactoring anything. Therefore, I have director, gather_led, and gather_law.
Director requests the user select a calculation to perform, and then returns a call to gather_led or gather_law. The gather functions handle information gathering, then returns a call to the appropriate function. Long story short: the result of the calculation functions are returned to the caller of director.