Jump to content
The simFlight Network Forums

Custom Radio Panel with Keypad and OLED Display – Help with Lua event.com Integration


Recommended Posts

Posted

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:

 

image.thumb.jpeg.8ca2e7bdbbdddf8ed6cf9f0bb7fe9803.jpeg

 

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

Posted
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

Posted

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

Posted

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

Posted
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

Posted

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.