Radiocraft Wireless M-Bus extension module  1.00.00
example.vpl
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2 // example.vpl created 2018-01-19 10:01
3 //-----------------------------------------------------------------------------
4 INCLUDE rtcu.inc
5 // Uncomment math.inc to add math library support.
6 INCLUDE math.inc
7 
8 INCLUDE mbus.inc
9 
10 // Input variables that can be configured via the configuration dialog (These are global)
11 VAR_INPUT
12 END_VAR;
13 
14 // Output variables that can be configured via the configuration dialog (These are global)
15 VAR_OUTPUT
16 
17  AOUT : ARRAY[1 .. 4] OF INT; | Analog output
18 END_VAR;
19 
20 //-----------------------------------------------------------------------------
21 // DataToHex
22 //-----------------------------------------------------------------------------
23 FUNCTION DataToHex : STRING;
24 VAR_INPUT
25  data : PTR;
26  datasize : INT;
27  length : INT;
28  reverse : BOOL := FALSE;
29 END_VAR;
30 VAR
31  src : SINT;
32  v : INT;
33  i,j : INT;
34  str_len : INT;
35  bstr : STRING;
36 END_VAR;
37 
38 // copy src to tmp
39 str_len := 0;
40 FOR i := 0 TO (datasize-1) DO
41  // Copy contents of one memory area to another area
42  memcpy(dst:=ADDR(src), src:=(data + i), len:=1);
43  bstr := "";
44  FOR j := 0 TO 1 DO
45  v := SINT(src AND 16#0F);
46  src := shr8(in := src, n := 4);
47  CASE (v) OF
48  0..9: bstr := intToStr(v := v) + bstr;
49  10 : bstr := "A" + bstr;
50  11 : bstr := "B" + bstr;
51  12 : bstr := "C" + bstr;
52  13 : bstr := "D" + bstr;
53  14 : bstr := "E" + bstr;
54  15 : bstr := "F" + bstr;
55  END_CASE;
56 
57  str_len := str_len + 1;
58  IF (str_len >= 254) THEN
59  RETURN;
60  END_IF;
61  END_FOR;
62  IF reverse THEN
63  DataToHex := DataToHex + bstr;
64  ELSE
65  DataToHex := bstr + DataToHex;
66  END_IF;
67 END_FOR;
68 
69 // fill with '0'
70 WHILE ((str_len < 254) AND (str_len < length)) DO
71  IF reverse THEN
72  DataToHex := DataToHex + "0";
73  ELSE
74  DataToHex := "0" + DataToHex;
75  END_IF;
76  str_len := str_len + 1;
77 END_WHILE;
78 END_FUNCTION;
79 
80 //-----------------------------------------------------------------------------
81 // DintToHex
82 //-----------------------------------------------------------------------------
83 FUNCTION DintToHex : STRING;
84 VAR_INPUT
85  v : DINT;
86  length : INT := 8;
87  reverse : BOOL := FALSE;
88 END_VAR;
89 DintToHex := DataToHex(data := ADDR(v), datasize:= SIZEOF(v), length := length, reverse := reverse);
90 END_FUNCTION;
91 
92 //-----------------------------------------------------------------------------
93 // IntToHex
94 //-----------------------------------------------------------------------------
95 FUNCTION IntToHex : STRING;
96 VAR_INPUT
97  v : INT;
98  length : INT := 4;
99  reverse : BOOL := FALSE;
100 END_VAR;
101 IntToHex := DataToHex(data := ADDR(v), datasize:= SIZEOF(v), length := length, reverse := reverse);
102 END_FUNCTION;
103 
104 //-----------------------------------------------------------------------------
105 // SintToHex
106 //-----------------------------------------------------------------------------
107 FUNCTION SintToHex : STRING;
108 VAR_INPUT
109  v : SINT;
110  length : INT := 2;
111  reverse : BOOL := FALSE;
112 END_VAR;
113 SintToHex := DataToHex(data := ADDR(v), datasize:= SIZEOF(v), length := length, reverse := reverse);
114 END_FUNCTION;
115 
116 //-----------------------------------------------------------------------------
117 // Converts the raw value to a string with the voltage
118 //-----------------------------------------------------------------------------
119 FUNCTION convData:STRING;
120 VAR_INPUT
121  data : PTR;
122 END_VAR;
123 VAR
124  val:DINT := 0;
125  v:FLOAT;
126 END_VAR;
127  // Copy raw value to DINT
128  memcpy(src:=data, dst:=ADDR(val), len:=3);
129  // convert raw value to voltage.
130  v := FLOAT(val) * 2.048*337.3/(exp2(v:=20.0)*67.3);
131  convData := strLeft(str:=floatToStr(v:=v), length := 5)+" V";
132 END_FUNCTION;
133 
134 //-----------------------------------------------------------------------------
135 // Get the three-letter manufaturer code from the raw number
136 //-----------------------------------------------------------------------------
137 FUNCTION convMan : STRING;
138 VAR_INPUT
139  man : INT;
140 END_VAR;
141 VAR
142  str:STRING;
143  data:ARRAY[1..3] OF SINT;
144 END_VAR;
145  data[1] := sinT(((man/(32*32))MOD 32)+64);
146  data[2] := sinT(((man/(32))MOD 32)+64);
147  data[3] := sinT(((man)MOD 32)+64);
148 
149  convMan := strFromMemory(src:=ADDR(data), len := 3);
150 END_FUNCTION;
151 
152 //-----------------------------------------------------------------------------
153 // Parse the ADC data and print the individual fields to the output.
154 //-----------------------------------------------------------------------------
155 FUNCTION ParseADC;
156 VAR_INPUT
157  data : PTR;
158 END_VAR;
159 VAR
160  vals:INT;
161  i:INT;
162  enc: INT;
163 END_VAR;
164  memcpy (src:=data+5, dst:=ADDR(enc),len:=2);
165 
166  DebugFmt(message := "ADC"
167  + ", A# " +DataToHex(data := data+1, datasize := 1, length := 2)
168  + ", St " +DataToHex(data := data+2, datasize := 1, length := 2)
169  + ", Sig " +DataToHex(data := data+3, datasize := 2, length := 4)
170  + ", enc " +IntToHex(v:=enc)
171  );
172  IF enc = 16#2f2f THEN
173  // Data is valid, print it
174  DebugFmt(message:="Data Type: "+DataToHex(data := data+7, datasize := 1, length := 2)
175  +", pVIF: "+DataToHex(data := data+8, datasize := 1, length := 2)
176  +", sVIF: "+DataToHex(data := data+9, datasize := 1, length := 2)
177  +", Value: "+DataToHex(data := data+10, datasize := 3, length := 6)
178  );
179  DebugFmt(message:="Val: "+convData(data:=data+10));
180  END_IF;
181 
182 END_FUNCTION;
183 
184 //-----------------------------------------------------------------------------
185 // Parse the ADC log data and print the individual fields to the output.
186 //-----------------------------------------------------------------------------
187 FUNCTION ParseLog;
188 VAR_INPUT
189  data : PTR;
190 END_VAR;
191 VAR
192  vals:INT;
193  i:INT;
194  enc: INT;
195 END_VAR;
196  memcpy (src:=data+6, dst:=ADDR(enc),len:=2);
197 
198  DebugFmt(message := "Log: "
199  + "Func "+DataToHex(data := data+1, datasize := 1, length := 2)
200  + ", A# " + DataToHex(data := data+2, datasize := 1, length := 2)
201  + ", St " + DataToHex(data := data+3, datasize := 1, length := 2)
202  + ", Sig " +DataToHex(data := data+4, datasize := 2, length := 4)
203  + ", enc " +IntToHex(v:=enc)
204  );
205 
206  IF enc = 16#2f2f THEN
207  DebugFmt(message:="His Type: "+DataToHex(data := data+8, datasize := 1, length := 2)
208  +", pVIF: "+DataToHex(data := data+9, datasize := 1, length := 2)
209  +", sVIF: "+DataToHex(data := data+10, datasize := 1, length := 2)
210  +", ValT: "+DataToHex(data := data+11, datasize := 1, length := 2)
211  +", Smpl: "+DataToHex(data := data+12, datasize := 3, length := 6)
212  );
213 
214  DebugFmt(message:=
215  "His#: "+DataToHex(data := data+15, datasize := 1, length := 2)
216  +", Time: "+DataToHex(data := data+16, datasize := 3, length := 6)
217  +", Part: "+DataToHex(data := data+19, datasize := 1, length := 2)
218  +", Vals: "+DataToHex(data := data+20, datasize := 1, length := 2)
219  );
220  memcpy(src := data+20, dst := ADDR(vals), len := 1);
221  DebugFmt(message:="vals: \1, "+DataToHex(data := ADDR(vals), datasize := 1, length := 2), v1:=vals);
222  for i := 0 to vals-1 DO
223  DebugFmt(message:=
224  " [\1]: "+DataToHex(data := data+21+i*6, datasize := 3, length :=6)
225  +", "+DataToHex(data := data+24+i*6, datasize := 3, length :=6)
226  +", "+convData(data:=data+24+i*6)
227  , v1 := i);
228 
229  END_FOR;
230  END_IF;
231 
232 END_FUNCTION;
233 
234 //-----------------------------------------------------------------------------
235 // Wireless MBUS monitor thread
236 //-----------------------------------------------------------------------------
237 THREAD_BLOCK tbMbusCollector;
238 VAR_INPUT
239  run : MUTABLE BOOL := TRUE;
240  init : MUTABLE BOOL := TRUE; // load extension module upon start
241  echo : BOOL := FALSE; // echo received data
242 END_VAR;
243 
244 VAR
245  frame : mbusFrame;
246  rc : INT;
247  pre : STRING;
248  type : STRING;
249  ci: SINT;
250  error_count : INT := 0;
251 END_VAR;
252 pre := strFormat(format := "MBUS(\1): ", v1 := thGetID());
253 DebugFmt(message := pre + "Started");
254 IF init THEN
255  init := FALSE;
256 
257  rc := mbusInit(showTags := TRUE);
258  DebugFmt(message := pre + "init = \1", v1 := rc);
259 END_IF;
260 
261 rc := mbusOpen(mode := MBUS_MODE_T1, RSSI:=ON);
262 DebugFmt(message := pre + "open = \1", v1 := rc);
263 // Receive data from all sensors, also sensors that are not configured
264 rc := mbusSetFilter(installed:=FALSE);
265 DebugFmt(message := pre + "set filter = \1", v1 := rc);
266 
267 IF echo THEN
268  DebugMsg(message := pre + "received frames are echo'ed");
269 END_IF;
270 
271 DebugFmt(message := pre + "Running");
272 WHILE run DO
273  rc := mbusReceive(frame := frame);
274  IF (rc < 0) THEN
275  DebugFmt(message := pre + "failed to to fetch data (err \1)", v1 := rc);
276  error_count := error_count +1;
277  if error_count > 3 THEN
278  run := FALSE;
279  END_IF;
280  ELSIF (rc > 0) THEN
281  error_count := 0;
282  DebugFmt(message := "Man : "+ convMan(man:=frame.manufacturer)+" (" + intToHex(v:=frame.manufacturer, reverse := false) + ")");
283  DebugFmt(message:="Length: \1, RSSI: \2 ("+floatToStr(v:=-FLOAT(frame.rssi) / 2.0)+" dB)", v1:=frame.length, v2:=frame.rssi);
284  CASE frame.type OF
285  16#19: type := "ADC"; // AD Converter
286  ELSE
287  type := "0x" + DataToHex(data := ADDR(frame.type), datasize := SIZEOF(frame.type), length := 2);
288  END_CASE;
289  DebugFmt(message := type + " : " + DintToHex(v:=frame.id, reverse := false) + " => " +
290  DataToHex(data := ADDR(frame.data), datasize := frame.length, reverse := true));
291  if frame.type = 16#19 THEN
292  memcpy(dst:=ADDR(ci), src := ADDR(frame.data), len := 1);
293  DebugFmt(message :=
294  " CI-field: " + DataToHex(data := ADDR(ci), datasize := 1, length := 2));
295  case ci OF
296  122://16#7A as decimal
297  ParseADC(data := ADDR(frame.data));
298 
299  -83://16#AD as decimal
300  ParseLog(data := ADDR(frame.data));
301  ELSE
302  DebugFmt(message:="Unknown message");
303  END_CASE;
304 
305  END_IF;
306 
307  // Echo back data
308  //mbusSend(control := frame.control, data := ADDR(frame.data), length := frame.length);
309  ELSE
310  // no data
311  Sleep(delay := 500);
312  END_IF;
313 END_WHILE;
314 DebugFmt(message := pre + "Stopping");
315 
316 rc := mbusClose();
317 DebugFmt(message := pre + "close = \1", v1 := rc);
318 
319 DebugFmt(message := pre + "Stopped");
320 END_THREAD_BLOCK;
321 
322 //-----------------------------------------------------------------------------
323 // The RTCU Application
324 //-----------------------------------------------------------------------------
325 PROGRAM mod_mbus;
326 VAR
327  collector : tbMbusCollector;
328  sms : gsmIncomingSMS;
329 
330  rc : INT;
331  index : INT;
332 
333  cmd : STRING;
334  text : STRING;
335  tag : STRING;
336 
337  key : ARRAY [1..16] OF SINT;
338 END_VAR;
339 Sleep(delay := 1000);
340 DebugMsg(message := "booting");
341 
342 collector.run := TRUE;
343 
344 DebugMsg(message := "ready");
345 
346 // Encryption key. It is normally unique for each slave.
347  key[1] := 16#00;
348  key[2] := 16#00;
349  key[3] := 16#00;
350  key[4] := 16#00;
351  key[5] := 16#00;
352  key[6] := 16#00;
353  key[7] := 16#00;
354  key[8] := 16#00;
355  key[9] := 16#00;
356  key[10] := 16#00;
357  key[11] := 16#00;
358  key[12] := 16#00;
359  key[13] := 16#00;
360  key[14] := 16#00;
361  key[15] := 16#00;
362  key[16] := 16#00;
363 
364 BEGIN
365  sms();
366  IF (sms.status > 0) THEN
367  DebugMsg(message := "SMS: '"+sms.message+"'");
368  FOR index := 1 TO 10 DO
369  // Extract a string from another delimited string
370  cmd := strToken(str:=sms.message, delimiter:=";", index:=index);
371  //DebugMsg(message := "cmd = '"+cmd+"'");
372  IF (cmd = "") THEN
373  EXIT;
374  ELSIF (cmd = "collect") THEN
375  collector.run := NOT collector.run;
376 
377  ELSIF (cmd = "open") THEN
378  rc := mbusOpen();
379  DebugFmt(message := "MBUS: open = \1", v1 := rc);
380 
381  ELSIF (cmd = "close") THEN
382  rc := mbusClose();
383  DebugFmt(message := "MBUS: close = \1", v1 := rc);
384 
385  ELSIF (cmd = "send") THEN
386  index := INT(random(lower := -32768, upper := 32767));
387  rc := mbusSend(control := 16#44, data := ADDR(index), length := SIZEOF(index));
388  DebugFmt(message := "MBUS: send '" + intToHex(v:=index) + "'= \1", v1 := rc);
389 
390  ELSIF (cmd = "ack") THEN
391  rc := mbusSend(control := 0, length := 0);
392  DebugFmt(message := "MBUS: ack = \1", v1 := rc);
393 
394  ELSIF (cmd = "info") THEN
395  rc := mbusInfo();
396  DebugFmt(message := "MBUS: info = \1", v1 := rc);
397 
398  ELSIF (cmd = "reset") THEN
399  DebugMsg(message := "resetting");
400  boardReset();
401  ELSIF (cmd = "reg") THEN
402  DebugMsg(message := "register sensors");
403 
404  rc := mbusRegisterSlave(idx := 1, manufacturer:=16#0646, id:=16#10000530, version:=1, type := 16#19, key := ADDR(key));
405  DebugFmt(message := "MBUS: reg = \1", v1 := rc);
406 
407  rc := mbusRegisterSlave(idx := 2, manufacturer:=16#0646, id:=16#20000530, version:=1, type := 16#19, key := ADDR(key));
408  DebugFmt(message := "MBUS: reg = \1", v1 := rc);
409 
410  ELSIF (cmd = "AOUT[2]") THEN
411  AOUT[2] := AOUT[2] + (1023/4);
412  IF (AOUT[2] > 1023) THEN AOUT[2] := 0; END_IF;
413 
414  ELSIF (cmd = "AOUT[1]") THEN
415  AOUT[1] := AOUT[1] + (1023/4);
416  IF (AOUT[1] > 1023) THEN AOUT[1] := 0; END_IF;
417 
418  END_IF;
419  END_FOR;
420  END_IF;
421 
422  IF collector.run AND NOT collector._running THEN
423  collector();
424  END_IF;
425 END;
426 DebugMsg(message := "stopped");
427 END_PROGRAM;