Isak Posted Thursday at 10:03 PM Report Posted Thursday at 10:03 PM Hi, I've built a small box with a 4x4 keypad and a 20x4 character OLED display. I’m using it to display the active and standby COM1 frequencies, and to type in the next standby frequency to be set (via Button A). In addition, I can set the standby frequencies for COM2, NAV1, and NAV2 using Buttons B, C, and D. If I make a mistake while typing, I can delete the last character by pressing "*". The "#" button functions as a transfer switch, toggling the COM1 standby and active frequencies. Here’s what the prototype looks like: I'd now like to clean up the Lua script and refactor it to use the event.com() function for handling serial input. Could anyone have a look at the script and maybe offer some suggestions on how to best achieve this? Here is the Lua script: ---------------------------------------------------------------- ---------------------------------------------------------------- dev = com.open("COM6", 115200, 0) -- Open COM port with baud rate 115200 ---------------------------------------------------------------- ---------------------------------------------------------------- -- Display a message to indicate that the script is running ipc.display("Keypad Radio - Lua script is running...", 4) -- Message stays for 4 seconds function processInput() local datastring, length = com.read(dev, 256) -- Read up to 256 bytes from the serial port -- Parsing the received string to set COM1 standby frequency if datastring:match("^COM1SB") then local freq_mhz = tonumber(datastring:sub(8)) -- Extract frequency in MHz if freq_mhz then local base = math.floor(freq_mhz) local fraction = freq_mhz - base -- Round fraction to nearest kHz to avoid float precision issues local frac_khz = math.floor(fraction * 1000 + 0.5) if frac_khz == 10 then freq_mhz = base + 0.011 elseif frac_khz == 15 then freq_mhz = base + 0.016 elseif frac_khz == 40 then freq_mhz = base + 0.041 elseif frac_khz == 80 then freq_mhz = base + 0.081 end local frequency = math.floor(freq_mhz * 1000000 + 0.5) -- Convert to Hz and round ipc.control(67363, frequency) end --[[ Before using the above code to round fraction to nearest kHz to avoid float precision issues, there were issues with the frequencies ending with x10, x15, x40 and x80 that the wrong frequency was being sent. x10 was sent as x09, x15 -> x14, x40 -> x39. The "old" code was these two lines: local frequency = tonumber(datastring:sub(8)) * 1000000 -- Extract frequency and convert to Hz ipc.control(67363, frequency) -- COM1_STBY_RADIO_HZ_SET ]] -- Parsing the received string to set COM2 standby frequency elseif datastring:match("^COM2SB") then local freq_mhz = tonumber(datastring:sub(8)) -- Extract frequency in MHz if freq_mhz then local base = math.floor(freq_mhz) local fraction = freq_mhz - base -- Round fraction to nearest kHz to avoid float precision issues local frac_khz = math.floor(fraction * 1000 + 0.5) if frac_khz == 10 then freq_mhz = base + 0.011 elseif frac_khz == 15 then freq_mhz = base + 0.016 elseif frac_khz == 40 then freq_mhz = base + 0.041 elseif frac_khz == 80 then freq_mhz = base + 0.081 end local frequency = math.floor(freq_mhz * 1000000 + 0.5) -- Convert to Hz and round ipc.control(67364, frequency) end --local frequency = tonumber(datastring:sub(8)) * 1000000 -- Extract frequency and convert to Hz --ipc.control(67364, frequency) -- COM2_STBY_RADIO_HZ_SET -- Parsing the received string to set NAV1 standby frequency elseif datastring:match("^NAV1SB") then local frequencyStr = datastring:match("NAV1SB%s*(%d+%.%d+)") -- Extracts the frequency if frequencyStr then ipc.log("Extracted frequency: " .. frequencyStr) -- Debug log local bcdValue = convertToBCD(frequencyStr) ipc.log("BCD Converted: " .. string.format("0x%X", bcdValue)) -- Debug log ipc.writeUD(0x311E, bcdValue) -- Send to FSUIPC ipc.display("NAV1 standby set: " .. frequencyStr, 3) ipc.log("NAV1 standby frequency set to BCD: " .. string.format("0x%X", bcdValue)) else ipc.log("Error: Failed to extract frequency!") end -- Parsing the received string to set NAV2 standby frequency elseif datastring:match("^NAV2SB") then local frequencyStr = datastring:match("NAV2SB%s*(%d+%.%d+)") -- Extracts the frequency if frequencyStr then ipc.log("Extracted frequency: " .. frequencyStr) -- Debug log local bcdValue = convertToBCD(frequencyStr) ipc.log("BCD Converted: " .. string.format("0x%X", bcdValue)) -- Debug log ipc.writeUD(0x3120, bcdValue) -- Send to FSUIPC ipc.display("NAV2 standby set: " .. frequencyStr, 3) ipc.log("NAV2 standby frequency set to BCD: " .. string.format("0x%X", bcdValue)) else ipc.log("Error: Failed to extract frequency!") end -- Transfer Standby to Active elseif datastring:match("^COM1TX") then ipc.control(66372) -- COM1_STBY_RADIO_SWAP end end -- Convert frequency to BCD (e.g., "113.45" → 0x1345) function convertToBCD(freq) local cleanFreq = freq:gsub("%.", "") -- Remove decimal (e.g., "109.2" → "0920") local decimalNumber = tonumber(cleanFreq) -- Convert to number if not decimalNumber then return 0 end -- Return 0 if conversion fails local bcd = 0 local shift = 0 -- Convert each decimal digit to BCD format while decimalNumber > 0 do local digit = decimalNumber % 10 bcd = bcd + (digit * (16 ^ shift)) -- Use base 16 shift instead of bitwise left shift decimalNumber = math.floor(decimalNumber / 10) shift = shift + 1 end return bcd end -- Format Hz to string (e.g. 128325000 -> "128.325") function formatComFreq(freqHz) return string.format("%.3f", freqHz / 1000000.0) end lastCom1 = "" lastCom1sb = "" function sendComFrequencies() local freqCom1 = ipc.readUD(0x05C4) -- COM1 active in Hz local strCom1 = formatComFreq(freqCom1) if strCom1 ~= lastCom1 then com.write(dev, "COM1:" .. strCom1 .. "\n") lastCom1 = strCom1 end local freqCom1sb = ipc.readUD(0x05CC) -- COM1 standby in Hz local strCom1sb = formatComFreq(freqCom1sb) if strCom1sb ~= lastCom1sb then com.write(dev, "COM1SB:" .. strCom1sb .. "\n") lastCom1sb = strCom1sb end end --Main loop to continuously check for serial input while true do processInput() -- Call the function to process any incoming serial data ipc.sleep(100) -- Small delay to avoid high CPU usage sendComFrequencies() -- Call the function to process any incoming serial data ipc.sleep(100) -- Small delay to avoid high CPU usage end --event.com(dev, 3, 1, 10, "processInput") --event.timer(1000, "sendComFrequencies") ---------------termination----------------- function closeCom () com.close(dev) end And here is the Arduino code: #include <Keypad.h> #include <LiquidCrystal.h> // Initialize the LCD connected to pins 9, 8, 7, 6, 5, 4 on Arduino LiquidCrystal lcd(9, 8, 7, 6, 5, 4); // Keypad settings const byte ROWS = 4; // four rows const byte COLS = 4; // four columns char keys[ROWS][COLS] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; byte rowPins[ROWS] = {A0, A1, A2, A3}; byte colPins[COLS] = {A4, A5, A6, A7}; Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); String frequency = "1"; // Pre-define the first digit as '1' String inputString = ""; bool stringComplete = false; void setup() { lcd.begin(20, 4); // Turn off the display: lcd.noDisplay(); delay(500); // Turn on the display: lcd.display(); // Initial LCD message: lcd.setCursor(4, 1); lcd.print("KEYPAD RADIO"); delay(4000); lcd.clear(); lcd.setCursor(0, 0); lcd.print("COM1 ACT"); lcd.setCursor(0, 1); lcd.print("COM1 STB"); lcd.setCursor(0, 3); lcd.print("NEXT STB"); Serial.begin(115200); //start checking serial connection } void loop() { char key = keypad.getKey(); if (key) { if (key == 'A') { sendCom1FrequencyToSim(); frequency = "1"; // Reset to '1' after sending lcd.setCursor(9, 3); lcd.print(" "); } else if (key == 'B') { sendCom2FrequencyToSim(); frequency = "1"; // Reset to '1' after sending lcd.setCursor(9, 3); lcd.print(" "); } else if (key == 'C') { sendNav1FrequencyToSim(); frequency = "1"; // Reset to '1' after sending lcd.setCursor(9, 3); lcd.print(" "); } else if (key == 'D') { sendNav2FrequencyToSim(); frequency = "1"; // Reset to '1' after sending lcd.setCursor(9, 3); lcd.print(" "); } else if (key == '*') { if (frequency.length() > 1) { // Ensure '1' stays as the first digit frequency.remove(frequency.length() - 1); // Remove last digit // Clear the previous frequency display and update it lcd.setCursor(9, 3); lcd.print(" "); lcd.setCursor(9, 3); lcd.print(formatFrequency(frequency)); } } else if (key == '#') { transferFrequency(); // Trigger COM1 STBY RADIO SWAP } else if (isDigit(key)) { if (frequency.length() < 6) { // limit to 6 digits (including '1') frequency += key; // Update the LCD with the new frequency lcd.setCursor(9, 3); lcd.print(formatFrequency(frequency)); } } } else if (stringComplete) { if (inputString.startsWith("COM1:")) { String freq = inputString.substring(5); lcd.setCursor(9, 0); lcd.print(" "); // Clear lcd.setCursor(9, 0); lcd.print(freq); } else if (inputString.startsWith("COM1SB:")) { String freq = inputString.substring(7); lcd.setCursor(9, 1); lcd.print(" "); // Clear lcd.setCursor(9, 1); lcd.print(freq); } inputString = ""; stringComplete = false; } } String formatFrequency(String freq) { // Automatically add the decimal point at the right place if (freq.length() >= 4) { // Adjusted to account for the pre-defined '1' return freq.substring(0, 3) + "." + freq.substring(3); } return freq; // If less than 3 digits, no decimal } void sendCom1FrequencyToSim() { String formattedFrequency = formatFrequency(frequency); // Add the decimal point Serial.print("COM1SB "); // Add COM1SB tag Serial.println(formattedFrequency); // Send formatted frequency (with decimal) } void sendCom2FrequencyToSim() { String formattedFrequency = formatFrequency(frequency); // Add the decimal point Serial.print("COM2SB "); // Add COM2SB tag Serial.println(formattedFrequency); // Send formatted frequency (with decimal) } void sendNav1FrequencyToSim() { String formattedFrequency = formatFrequency(frequency); // Add the decimal point Serial.print("NAV1SB "); // Add NAV1SB tag Serial.println(formattedFrequency); // Send formatted frequency (with decimal) } void sendNav2FrequencyToSim() { String formattedFrequency = formatFrequency(frequency); // Add the decimal point Serial.print("NAV2SB "); // Add NAV2SB tag Serial.println(formattedFrequency); // Send formatted frequency (with decimal) } void transferFrequency() { Serial.println("COM1TX"); // Send transfer command } void serialEvent() { while (Serial.available()) { char inChar = (char)Serial.read(); if (inChar == '\n') { stringComplete = true; } else { inputString += inChar; } } } Cheers, Isak
John Dowson Posted Friday at 08:36 AM Report Posted Friday at 08:36 AM 10 hours ago, Isak said: I'd now like to clean up the Lua script and refactor it to use the event.com() function for handling serial input. Could anyone have a look at the script and maybe offer some suggestions on how to best achieve this? Change 10 hours ago, Isak said: function processInput() local datastring, length = com.read(dev, 256) -- Read up to 256 bytes from the serial port -- Parsing the received string to set COM1 standby frequency ... to function processInput(dev, datastring, length) -- Parsing the received string to set COM1 standby frequency ... and wait for the data event using event.com(dev, 256, "processInput") For the sendComFrequencies function, you can use event.Offset on offsets 0x05C4 and 0x5CC, and refactor the function to use the value passed in with a conditional on the offset (or use two functions, one for each offset. John
Isak Posted Friday at 08:28 PM Author Report Posted Friday at 08:28 PM Thanks for your quick response, John. I tried modifying the Lua script based on your hints and explanation. However, the script no longer runs. Here's the updated Lua script: ---------------------------------------------------------------- ---------------------------------------------------------------- dev = com.open("COM6", 115200, 0) -- Open COM port with baud rate 115200 ---------------------------------------------------------------- ---------------------------------------------------------------- -- Display a message to indicate that the script is running ipc.display("Keypad Radio - Lua script is running...", 4) -- Message stays for 4 seconds function processInput(dev, datastring, length) -- Parsing the received string to set COM1 standby frequency if datastring:match("^COM1SB") then local freq_mhz = tonumber(datastring:sub(8)) -- Extract frequency in MHz if freq_mhz then local base = math.floor(freq_mhz) local fraction = freq_mhz - base -- Round fraction to nearest kHz to avoid float precision issues local frac_khz = math.floor(fraction * 1000 + 0.5) if frac_khz == 10 then freq_mhz = base + 0.011 elseif frac_khz == 15 then freq_mhz = base + 0.016 elseif frac_khz == 40 then freq_mhz = base + 0.041 elseif frac_khz == 80 then freq_mhz = base + 0.081 end local frequency = math.floor(freq_mhz * 1000000 + 0.5) -- Convert to Hz and round ipc.control(67363, frequency) end --[[ Before using the above code to round fraction to nearest kHz to avoid float precision issues, there were issues with the frequencies ending with x10, x15, x40 and x80 that the wrong frequency was being sent. x10 was sent as x09, x15 -> x14, x40 -> x39. The "old" code was these two lines: local frequency = tonumber(datastring:sub(8)) * 1000000 -- Extract frequency and convert to Hz ipc.control(67363, frequency) -- COM1_STBY_RADIO_HZ_SET ]] -- Parsing the received string to set COM2 standby frequency elseif datastring:match("^COM2SB") then local freq_mhz = tonumber(datastring:sub(8)) -- Extract frequency in MHz if freq_mhz then local base = math.floor(freq_mhz) local fraction = freq_mhz - base -- Round fraction to nearest kHz to avoid float precision issues local frac_khz = math.floor(fraction * 1000 + 0.5) if frac_khz == 10 then freq_mhz = base + 0.011 elseif frac_khz == 15 then freq_mhz = base + 0.016 elseif frac_khz == 40 then freq_mhz = base + 0.041 elseif frac_khz == 80 then freq_mhz = base + 0.081 end local frequency = math.floor(freq_mhz * 1000000 + 0.5) -- Convert to Hz and round ipc.control(67364, frequency) end --local frequency = tonumber(datastring:sub(8)) * 1000000 -- Extract frequency and convert to Hz --ipc.control(67364, frequency) -- COM2_STBY_RADIO_HZ_SET -- Parsing the received string to set NAV1 standby frequency elseif datastring:match("^NAV1SB") then local frequencyStr = datastring:match("NAV1SB%s*(%d+%.%d+)") -- Extracts the frequency if frequencyStr then ipc.log("Extracted frequency: " .. frequencyStr) -- Debug log local bcdValue = convertToBCD(frequencyStr) ipc.log("BCD Converted: " .. string.format("0x%X", bcdValue)) -- Debug log ipc.writeUD(0x311E, bcdValue) -- Send to FSUIPC ipc.display("NAV1 standby set: " .. frequencyStr, 3) ipc.log("NAV1 standby frequency set to BCD: " .. string.format("0x%X", bcdValue)) else ipc.log("Error: Failed to extract frequency!") end -- Parsing the received string to set NAV2 standby frequency elseif datastring:match("^NAV2SB") then local frequencyStr = datastring:match("NAV2SB%s*(%d+%.%d+)") -- Extracts the frequency if frequencyStr then ipc.log("Extracted frequency: " .. frequencyStr) -- Debug log local bcdValue = convertToBCD(frequencyStr) ipc.log("BCD Converted: " .. string.format("0x%X", bcdValue)) -- Debug log ipc.writeUD(0x3120, bcdValue) -- Send to FSUIPC ipc.display("NAV2 standby set: " .. frequencyStr, 3) ipc.log("NAV2 standby frequency set to BCD: " .. string.format("0x%X", bcdValue)) else ipc.log("Error: Failed to extract frequency!") end -- Transfer Standby to Active elseif datastring:match("^COM1TX") then ipc.control(66372) -- COM1_STBY_RADIO_SWAP end end -- Convert frequency to BCD (e.g., "113.45" → 0x1345) function convertToBCD(freq) local cleanFreq = freq:gsub("%.", "") -- Remove decimal (e.g., "109.2" → "0920") local decimalNumber = tonumber(cleanFreq) -- Convert to number if not decimalNumber then return 0 end -- Return 0 if conversion fails local bcd = 0 local shift = 0 -- Convert each decimal digit to BCD format while decimalNumber > 0 do local digit = decimalNumber % 10 bcd = bcd + (digit * (16 ^ shift)) -- Use base 16 shift instead of bitwise left shift decimalNumber = math.floor(decimalNumber / 10) shift = shift + 1 end return bcd end -- Format Hz to string (e.g. 128325000 -> "128.325") function formatComFreq(freqHz) return string.format("%.3f", freqHz / 1000000.0) end lastCom1 = "" lastCom1sb = "" function sendComFrequencies() local freqCom1 = ipc.readUD(0x05C4) -- COM1 active in Hz local strCom1 = formatComFreq(freqCom1) if strCom1 ~= lastCom1 then com.write(dev, "COM1:" .. strCom1 .. "\n") lastCom1 = strCom1 end local freqCom1sb = ipc.readUD(0x05CC) -- COM1 standby in Hz local strCom1sb = formatComFreq(freqCom1sb) if strCom1sb ~= lastCom1sb then com.write(dev, "COM1SB:" .. strCom1sb .. "\n") lastCom1sb = strCom1sb end end -- Events List --initialize() event.com(dev, 256, "processInput") -- Radio event.offset(0x05C4, "UD", "sendComFrequencies") -- Com1 event.offset(0x05CC, "UD", "sendComFrequencies") -- Com1 StandBy ---------------termination----------------- -- Termination event.terminate("closeCom") Isak
John Dowson Posted yesterday at 09:16 AM Report Posted yesterday at 09:16 AM First, can you please attach your lua scripts rather than pasting the contents. It would make this thread far easier to read. 12 hours ago, Isak said: I tried modifying the Lua script based on your hints and explanation. However, the script no longer runs. As I said, you should update the sendComFrequencies fumction, e.g. function sendComFrequencies(offset, value) if offset == 0x05C4 then local strCom1 = formatComFreq(value) if strCom1 ~= lastCom1 then com.write(dev, "COM1:" .. strCom1 .. "\n") lastCom1 = strCom1 end else if offset == 0x05CC then local strCom1sb = formatComFreq(value) if strCom1sb ~= lastCom1sb then com.write(dev, "COM1SB:" .. strCom1sb .. "\n") lastCom1sb = strCom1sb end end end But if its not running, try debugging it. Check the FSUIPC log file for errors, add further ipc.log to determine what is happening, and you can also set Lua Plugin logging to trace what is happening in the lua script. John
John Dowson Posted yesterday at 11:42 AM Report Posted yesterday at 11:42 AM 2 hours ago, John Dowson said: As I said, you should update the sendComFrequencies fumction, e.g. function sendComFrequencies(offset, value) if offset == 0x05C4 then local strCom1 = formatComFreq(value) if strCom1 ~= lastCom1 then com.write(dev, "COM1:" .. strCom1 .. "\n") lastCom1 = strCom1 end else if offset == 0x05CC then local strCom1sb = formatComFreq(value) if strCom1sb ~= lastCom1sb then com.write(dev, "COM1SB:" .. strCom1sb .. "\n") lastCom1sb = strCom1sb end end end As the offset event handling function is only called when the offsets change, you don't need to store and compare to the old value, so this can be simplified to function sendComFrequencies(offset, value) if offset == 0x05C4 then com.write(dev, "COM1:" .. formatComFreq(value) .. "\n") else if offset == 0x05CC then com.write(dev, "COM1SB:" .. formatComFreq(value) .. "\n") end end end John
Isak Posted 21 hours ago Author Report Posted 21 hours ago Thank you, John, for your help and for simplifying the sendComFrequencies function. I’ll definitely attach the script files in the future to keep the thread easier to read. The logging showed that the last line in the script was causing the issue: event.terminate("closeCom") The problem was that the function it refers to was missing: function closeCom() com.close(dev) end Everything now works perfectly — both with and without the closeCom function. Would you recommend keeping the function in the script, or is it better to leave it out? Cheers, Isak
John Dowson Posted 4 hours ago Report Posted 4 hours ago Better to keep the terminate function - always a good idea to close com connections. Cheers, John 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now