Need help with interrupt on data received over serial.

Hello,

I am just starting to build an app on AAF. My first step is to write a stream of data out the serial port while waiting for any data to be received. If any data is received on the serial port I want to break and handle it. I imagine this should be done with emitters and events but I cannot find any documentation on how to implement these only that they exist.

My source is pretty simple right now, open the serial port, start a loop that writes to the serial(dummy data for now), and then ideally wait for any data to be received on the same port. You can find the code below.

Thanks!

local sched = require 'sched'
 
-- Load the module and store it inside a local variable
local lib = require 'my_module'

--- code from sample serial open, write and read
local devicetree = require 'devicetree'
local system     = require 'system'
local serial     = require "serial"  -- Serial lib
local os         = require "os"      -- Operating system lib
local serdev
-- Variable path to be used
local RESERVE_SER0 = "system.aleos.reserve.ser0"

--- Check if the serial port has been reserved for our use
-- if not, reserve it and reboot to gain control
function reserve_serial_port (asset)

    local serial_available = assert (devicetree.get(RESERVE_SER0))
    if serial_available ~= 1 then
        print ("WARNING","serial port not available, reserving it now...")
        local result = assert (devicetree.set(RESERVE_SER0, 1))
        print ("WARNING","Serial port Reserved ok, rebooting now...")
        system.reboot()
        sched.wait(30)  -- stop here until the reset occurs
    end
    print ("Serial Port has been reserved for our use.")
end

--------------------------------------------------------------------------------
-- open and configure the serial port
function open_serial_port (asset)

    local portname = "/dev/ttyS0"
    local serial_config = 
    {
        baudRate=19200,
        flowControl = 'none',
        numDataBits=8,
        parity='none',
        numStopBits=1
    }
    
    local serialdev = assert (serial.open(portname, serial_config), "Serial Port OPEN Failed!")
    return serialdev
end    

--------------------------------------------------------------------------------
-- Write to the serial port
function write_to_serial_port (sdevice, sdata)
    local write_cnt = assert (sdevice:write (sdata), "Serial Port WRITE Failed!")
    assert (sdevice:flush()) --- Added by ME in testing ...
    print ('Number of characters written to serial port: ', write_cnt)
end


--------------------------------------------------------------------------------
-- Read a line from the serial port
function read_serial_port (sdevice)
    
   -- assert (sdevice.settimeout (5,b))  -- set a timeout
    
    local read_data, Error = sdevice:read(1)    -- read a line
    if Error then
    	print ("Serial Port Read Failed because: ", Error)
    end
    return read_data
end
--------------------------------------------------------------------------------
-- Serial Sample - Examples of the serial port API usage
-- 

local function main()
	assert(devicetree.init())
	assert(system.init())
    sched.wait(1)
   
    reserve_serial_port ()
    sched.wait(1)
    
    serdev = open_serial_port ()
    sched.wait(1)
    
    write_to_serial_port (serdev, 'Serial port open, please enter data.\r\n')
    sched.wait(2)
    sched.run(WriteLoop(serdev))
    sched.run(ReadLoop(serdev))   
end
function WriteLoop(serial)
	while true do
		print('writeloop')
			write_to_serial_port(serial, 'WriteLoop')
			sched.wait(2)
	end
end
function ReadLoop(serial)
	local char = read_serial_port(serial)

   	if(char == "\000")
   		then
   		print ('break')
   	end
    print (char)
    
end
sched.run(main)
--sched.run(WriteLoop(serdev))
sched.loop()

Hi IanDrake,

A few unrelated comments:

  1. the serial:flush() API is mainly useful to flush/empty the read buffer. Not necessary to call it when writing data. You may use it if you want a ‘clean’ start when reading some data to empty possibly stored garbage in the read buffer.
  2. The read and write loop function should be declared with ‘local’ (they are currently set as global, possibly cluttering your global environment). But then they need to be declared before you use them so you would need to move the code ‘above’ the main() function where you call them with sched.run()

For your actual question I’d need more details on the goal. You could use sched.signal and sched.wait indeed. (please look at the doc, it is quite easy once you get it :slight_smile:)

An alternative would be to do your processing directly in the read loop. (technically you might not need a separate write loop)

Hello!

Thank you for your response! I will test out making the read and write loops local and declaring them before main.

I think using sched.signal and sched.wait are what I need to get the functionality I am looking for but I could not find documentation on using those properly. You say look at the doc would you mind pointing me in the direction of said doc? I have been reading through this: https://source.sierrawireless.com/resources/airlink/aleos_af/refdoc_aleos_af_api_1_3/#url=~/media/Support_Downloads/AirLink/AAF/aleos_af_api-13.0/sched.html&id=iframe0.

I came across this statement which again is what I am looking to do but not clear how to do it “Objects are encouraged to emit signals every time an event of interest happens to them, so that other tasks can react to these events.”

I feel as though I am missing something simple with these signals and once it clicks I will be off to the races ha. For now I have simplified my code a bit and all I am trying to do is send a signal when a break is received. Inside my read loop I have changed the code to the following:

local function ReadLoop(serial)
	local char = read_serial_port(serial)

   	if(char == "\000")
   		then
   		sched.signal('BREAK', 'TEST',1)
   	end
    print (char)
    
end

I assumed that when a break is received it will send out that signal, which should be picked up by this

sched.sigRun('BREAK', '*', function(event, arg)
	print("Break Signal Received" .. event .. ", arg " .. arg)
	end
	)

Edit : It looks like I was able to get that signal received! So I will have to play around with this a bit more. I am unsure where sched.wait is going to come into the equation though!

Hi,

Yes the doc you are referring to is the one I had in mind, thank you.
It looks like you figured it out.
sigrun helps schedule a new task when a signal is received. the wait(xxx, yyy) function blocks until xxx, yyy is received. So you could ahve a dedicated thread with a while loop and a sched.wait() inside it. I am not saying you have to do that, again it will depends on your use case. If you are OK with sigrun it is OK as well.

I think the ‘object’ part for the emitter is to make the signal more ‘specific’. It is perfectly OK to use a string instead as you did. The convention though is to have emitter, event.
So in your case the signal would be “SERIAL”, “BREAK” (instead of “BREAK”, “TEST”). But that is just a convention in the end.

Thank you very much! I was unsure on the convention for the signals and now that I see your post it makes a lot more sense why there were two variables required for that function.

After fiddling some more I was able to get the functionality required so thank you again!