summaryrefslogtreecommitdiff
path: root/rsbusserv.erl
blob: 2753bb0d64188226352c7c7574ae2bdc1f7226f0 (plain)
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
120
121
122
%%%-------------------------------------------------------------------
%%% @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/ttyACM0").
-define(SPEED,9600).
-define(TIMEOUT,10). % Timeout in milisec -> Also affect slaves' timeslice
-define(REQTIMEOUT,1000).

-define(STARTBIT,[16#5B,16#AD]).
-define(STOPBIT,[16#A4,16#52]).
-define(REP,16#FF).
-define(ERR,16#F0).

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([H1,H2,H3,H4|Tail],wait,[],Cmp) ->
  rsbus_rec([H2,H3,H4|Tail],wait,[],Cmp);
rsbus_rec(Rec,State,Msg,Cmp) ->
  receive
    {data, Bytes} ->
      %% io:format("~p\n",[Rec++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,[?ERR],RP} -> {error,parse};                    % State Reject
    {stat,Stat,RP} ->
      erlang:display(RP),
      TestP =rsbus_parity([RP|Stat]),
      if
        TestP /= Addr -> {error,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} ->
      Stat
  after ?REQTIMEOUT ->
    erlang:display("here"),
    {error,timeout}
  end.

start() ->
  spawn(?MODULE,rsbusmon,[]).