%%%------------------------------------------------------------------- %%% @author Joe Zhao %%% @copyright (C) 2014, %%% @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,50). % 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,[]).