Reputation: 4797
I have an arduino set up in a simple manner that read a potentiometer and writes the output to the serial connection.
void setup() {
// initialize serial communication
Serial.begin(9600);
}
void loop() {
// read the value of A0, divide by 4 and
// send it as a byte over the serial connection
Serial.write(analogRead(A0) / 4);
delay(20);
}
I would like to have a prolog program that monitors this stream in the background. I would like it to log certain events but also for me to query the stream at any time.
I dont really know where to begin to set this up. How do I actually read a stream, and then how do I have two process/threads (Not sure what to call them). Where one process monitors the stream and a second process runs the top level so I can query a dynamic database?
I imagine something like:
:-dynamic currentvalue/1.
startthread1 :-
loop.
loop :- read_serial(X),
check(X),
retractall(currentvalue(_)),
assertz(currentvalue(X)),
loop.
check(X):-
X>100,
get_time(Time),
write_file(Time,X).
check(_).
*I omit the def of write_file
I think I would then query:
?- thread_create(startthread1, Id, [alias(serial_mon)]).
And then I should still have the top level available for me to query `currentvalue/1' when I want to ?
Will this work? How do I define read_serial/1
. ? Also how would I stop the thread which I named serial_mon
?
update
I have changed the loop code to the following:
void loop() {
// read the value of A0, divide by 4 and
// send it as a byte over the serial connection
Serial.print(analogRead(A0) / 4);
Serial.print('.');
delay(100);
}
If I look at the serial monitor I now get output like this:
205.205.205.205. etc
Which I think I should be able to read by using read/2?
If my prolog code is simply:
go(File):-
open(File,read,Stream,[]),
read(Stream,X),
writeln(X),
close(Stream).
And I query :
?-go('/dev/ttyACM0').
I get no output. What have I done wrong?
Upvotes: 2
Views: 369
Reputation: 40768
Your basic idea is OK. However, there are a few very important points you need to think about.
This is the easier part: Regarding "how do I actually read a stream", there are several options, and they are the ones you already know from single-threaded applications. The cleanest one is to use library(pio)
by Ulrich Neumerkel, and simply write a DCG that transparently reads from a stream. Another good option is to simply use read/1
or read/2
to read a Prolog term from a stream. Of course, the latter option requires the sensor to actually emit Prolog terms that can be automatically processed by these predicates, while the former option is more flexible.
You can also use network sockets and set up a simple web-based interface for communication. For example, check out JSON libraries for a simple and portable format. In summary, there is no shortage of options to handle this.
Personally, I recommend staying to Prolog syntax for such exchanges, simply because you can more conveniently handle the data when it is written in Prolog. For example, if you write your log files as a series of Prolog facts, you can then easily consult them and run queries over them to generate reports. Any other format only makes this more complicated.
The second issue is much more subtle and regards race conditions between the threads. For example, in the code you show, there is a critical point in time where no data is available at all:
loop :- read_serial(X), check(X), retractall(currentvalue(_)), currentvalue/1 is not available at this point in time! assertz(currentvalue(X)), loop.
If the fetching thread queries currentvalue/1
at this critical time where it is not defined, it may unexpectedly fail.
For SWI-Prolog, closely read the chapter about thread synchronization to avoid such issues.
For example, we can implement it as:
loop :- read_serial(X), check(X), with_mutex(synch, (retractall(currentvalue(_)), assertz(currentvalue(X)))), loop.
In the querying thread, you would use with_mutex(synch, currentvalue(X))
to fetch the current value. This ensures that it will not happen during the critical point in time where the old value is already removed, but the new one is not yet asserted.
Note that both the toplevel and also any other thread can query these predicates. To spawn further threads, simply use thread_create/3
again with different goals.
Upvotes: 2