1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
%%%-------------------------------------------------------------------
%%% @author Joe Zhao
%%% @copyright (C) 2014, <COMPANY>
%%% @doc
%%% This server monitors behaviors on the serial bus.
%%% Whose duty includes serialize queries, message encoding,
%%% and response handling(only when slave request repitition of the previous command)
%%% The returns of the command are sent back to its origins(first parameter of the tuple, can also be someone else).
%%% @end
%%% Created : 27. 四月 2014 16:52
%%%-------------------------------------------------------------------
-module(rsbusserv).
-author("Joe Zhao").
-compile([debug_info,export_all]).
%% API
%% -export([start/0]).
-define(DEVICE,"/dev/ttyUSB0").
-define(SPEED,19200).
-define(TIMEOUT,40). % Timeout in milisec
-define(REQTIMEOUT,200).
-define(STARTBIT,[16#5B,16#AD]).
-define(STOPBIT,[16#A4,16#52]).
-define(REP,16#FF).
rsbuslistener(Pid) ->
SerialPort = serial:start([{speed,?SPEED},{open,?DEVICE}]),
register(?MODULE,self()),
rsbus_listner(Pid,SerialPort).
rsbus_parity(Msg) ->
lists:foldl(fun (X,P) -> X bxor P end,0,Msg).
rsbus_rec([],ok,Msg,[]) ->
{stat,lists:reverse(tl(Msg)),hd(Msg)};
rsbus_rec([H1,H2|Tail],read,Msg,[H1,H2]) ->
rsbus_rec([],ok,Msg,[]);
rsbus_rec([H1,H2,H3|Tail],read,Msg,Cmp) ->
rsbus_rec([H2,H3|Tail],read,[H1|Msg],Cmp);
rsbus_rec([H1,H2,H3,H4|Tail1],wait,[],[H1,H2,H3,H4]) ->
rsbus_rec(Tail1,read,[],?STOPBIT);
rsbus_rec([H|Tail],wait,[],Cmp) ->
rsbus_rec(Tail,wait,[],Cmp);
rsbus_rec(Rec,State,Msg,Cmp) ->
receive
{data, Bytes} ->
erlang:display(binary_to_list(Bytes)),
rsbus_rec(Rec++binary_to_list(Bytes),State,Msg,Cmp)
after ?TIMEOUT ->
{error,timeout}
end.
rsbus_send(SerialPort,Addr,Msg) ->
%% Return {stat,Type,Stat} upon success
%% else return {error,timeout|parse}
SerialPort ! {send,?STARTBIT},
SerialPort ! {send,[Addr]},
SerialPort ! {send,Msg},
P=rsbus_parity(Msg) bxor Addr,
SerialPort ! {send,[P]},
SerialPort ! {send,?STOPBIT},
io:format("Send Complete!\n"),
case rsbus_rec([],wait,[],?STARTBIT++[0,Addr]) of
{stat,[?REP],RP} -> rsbus_send(SerialPort,Addr,Msg); % Will resend no matter what
{stat,Stat,RP} ->
erlang:display(RP),
TestP =rsbus_parity([RP|Stat]),
if
TestP /= Addr -> {err,parse};
true -> {stat,hd(Stat),tl(Stat)}
end;
{error,timeout} -> {error,timeout}
end.
%% This function can effectively serialize access
rsbus_listner(Pid,SerialPort) ->
erlang:display(self()),
receive
{data, _Bytes} ->
%% This shouldn't happen, no process should initiate comm
%% Discard everything
rsbus_listner(Pid,SerialPort);
{PPid,comm,Addr,Msg} ->
io:format("RSBus sending..."),
PPid ! {self(),device,Addr,rsbus_send(SerialPort,Addr,Msg)}, % Process and return
rsbus_listner(Pid,SerialPort);
{Pid,shutdown} ->
SerialPort ! stop,
ok
end.
rsbusloop(Pid) ->
receive
{'EXIT',Pid,Cond} when Cond == normal; Cond==shutdown -> rsbusloop(spawn_link(?MODULE,rsbuslistener,[self()]));
{PPid,shutdown} -> Pid ! {self(),shutdown}
end.
rsbusmon() ->
process_flag(trap_exit,true),
Pid = spawn_link(?MODULE,rsbuslistener,[self()]),
rsbusloop(Pid).
rsbusreq(Addr,Msg) ->
rsbusserv ! {self(),comm,Addr,Msg},
receive
{PPid,device,Addr,{error,Reason}} ->
{error,Reason};
{PPid,device,Addr,{stat,Type,Msg}} ->
{stat,Type,Msg}
after ?REQTIMEOUT ->
{error,timeout}
end.
start() ->
spawn(?MODULE,rsbusmon,[]).
|