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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
dcf77_pin=6
led_pin=4
min_ambient_light=25
last_valid_rise=0
valid_rise_min=990000
valid_rise_max=1010000
valid_timeout=5000000
valid_newmin_min=1990000
valid_newmin_max=2100000
valid_0_min=100000
valid_0_max=140000
valid_1_min=200000
valid_1_max=250000
dcfdata=""
valid_times=0
total_times=0
bcd={}
bcd[1]=1
bcd[2]=2
bcd[3]=4
bcd[4]=8
bcd[5]=10
bcd[6]=20
bcd[7]=40
bcd[8]=80
dow={}
dow[0]="Invalid"
dow[1]="Mon"
dow[2]="Tue"
dow[3]="Wed"
dow[4]="Thu"
dow[5]="Fri"
dow[6]="Sat"
dow[7]="Sun"
mon_days={}
mon_days[1]=31
mon_days[2]=28
mon_days[3]=31
mon_days[4]=30
mon_days[5]=31
mon_days[6]=30
mon_days[7]=31
mon_days[8]=31
mon_days[9]=30
mon_days[10]=31
mon_days[11]=30
mon_days[12]=31
function bcd2dec(data)
local value=0
for count=1,string.len(data),1
do
if string.sub(data,count,count)=="1" and bcd[count]~=nil then
value=value+bcd[count]
end
end
return value
end
function check_parity(data)
local onecount=0
for count=1,string.len(data),1
do
if string.sub(data,count,count)=="1" then
onecount=onecount+1
end
end
if onecount%2==0 then
return true
end
return false
end
gps_sec=0
gps_valid=false
gps_part1=""
gps_part2=""
gps_part_checksum=0
hex={}
hex[0]="0"
hex[1]="1"
hex[2]="2"
hex[3]="3"
hex[4]="4"
hex[5]="5"
hex[6]="6"
hex[7]="7"
hex[8]="8"
hex[9]="9"
hex[10]="A"
hex[11]="B"
hex[12]="C"
hex[13]="D"
hex[14]="E"
hex[15]="F"
function send_gpsstring()
local gps_data
local gps_checksum=gps_part_checksum
local part1len=string.len(gps_part1)
if gps_sec<10 then
gps_data=gps_part1.."0"..gps_sec..gps_part2
else
gps_data=gps_part1..gps_sec..gps_part2
end
gps_checksum=bit.bxor(gps_checksum,string.byte(string.sub(gps_data,part1len+1,part1len+1)))
gps_checksum=bit.bxor(gps_checksum,string.byte(string.sub(gps_data,part1len+2,part1len+2)))
local digit1=bit.rshift(gps_checksum,4)
local digit2=bit.band(gps_checksum,15)
print("$"..gps_data.."*"..hex[digit1]..hex[digit2].."\r")
end
function parse_data(data)
total_times=total_times+1
--print("-------------------------------------------------------------")
--print(data)
--print("Resrv: ",string.sub(data,1,15))
--print("Info: ",string.sub(data,16,21))
--print("Min: ",string.sub(data,22,29),bcd2dec(string.sub(data,22,28)),check_parity(string.sub(data,22,29)))
--print("Hour: ",string.sub(data,30,36).." ",bcd2dec(string.sub(data,30,35)),check_parity(string.sub(data,30,36)))
--print("Day: ",string.sub(data,37,42).." ",bcd2dec(string.sub(data,37,42)))
--print("DOW: ",string.sub(data,43,45).." ",bcd2dec(string.sub(data,43,45)))
--print("Month: ",string.sub(data,46,50).." ",bcd2dec(string.sub(data,46,50)))
--print("Year: ",string.sub(data,51,59),bcd2dec(string.sub(data,51,59)),check_parity(string.sub(data,37,59)))
--print("Rest: ",string.sub(data,60))
--if string.len(data)==59 then
-- print("Time/date : "..bcd2dec(string.sub(data,30,35))..":"..bcd2dec(string.sub(data,22,28)).." "..dow[bcd2dec(string.sub(data,43,45))].." 20"..bcd2dec(string.sub(data,51,59)).."-"..bcd2dec(string.sub(data,46,50)).."-"..bcd2dec(string.sub(data,37,42)))
--end
gps_valid=false
local min
local hour
local day
local mon
local year
if data==nil then
--print("No data received")
return
end
if string.len(data)~=59 then
--print("Data length invalid")
return
end
--print("Data length valid")
if string.sub(data,18,18)==string.sub(data,19,19) then
--print("Timezone data is invalid")
return
end
--print("Timezone data is valid")
if string.sub(data,21,21)=="0" then
--print("Start time bit is invalid")
return
end
--print("Start time bit is valid")
if not check_parity(string.sub(data,22,29)) then
--print("Minute parity invalid")
return
end
--print("Minute parity valid")
min=bcd2dec(string.sub(data,22,28))
if min>59 then
--print("Minute value invalid")
return
end
--print("Minute value valid")
if not check_parity(string.sub(data,30,36)) then
--print("Hour parity invalid")
return
end
--print("Hour parity valid")
hour=bcd2dec(string.sub(data,30,35))
if hour>23 then
--print("Hour valid invalid")
return
end
--print("Hour valid valid")
if not check_parity(string.sub(data,37,59)) then
--print("Date parity invalid")
return
end
--print("Date parity valid")
day=bcd2dec(string.sub(data,37,42))
if day<1 or day>31 then
--print("Day value invalid")
return
end
--print("Day valud valid")
mon=bcd2dec(string.sub(data,46,50))
if mon<1 or mon>12 then
--print("Month value invalid")
return
end
--print("Month value valid")
year=bcd2dec(string.sub(data,51,59))
if year<22 or year>30 then
--print("Year value invalid")
return
end
--print("Year value valid")
valid_times=valid_times+1
--print("Total "..valid_times.." valid times and "..total_times-valid_times.." invalid times")
if string.sub(data,18,18)==1 then
--print("Subtracting 1 hour for CET timezone")
hour=hour-1
else
--print("Subtracting 2 hours for CEST timezone")
hour=hour-2
end
if hour<0 then
hour=hour+24
day=day-1
if day<1 then
mon=mon-1
if mon<1 then
mon=12
year=year-1
end
if mon==2 and year%4==0 then
day=day+1
end
day=mon_days[mon]
end
end
gps_part1=string.format("GPZDA,%02d%02d",hour,min)
gps_part2=string.format(".000,%02d,%02d,20%02d,,",day,mon,year)
local gps_data=gps_part1..gps_part2
gps_part_checksum=0
for count=1,string.len(gps_data)
do
gps_part_checksum=bit.bxor(gps_part_checksum,string.byte(string.sub(gps_data,count,count)))
end
gps_sec=0
gps_valid=true
send_gpsstring()
end
function irq_handler(level,when,count)
if count>10 then
--print("Waiting for stable signal, changing to polling")
start_poll()
return
end
if when<last_valid_rise then
delta=(2147483647-last_valid_rise)+when
else
delta=when-last_valid_rise
end
if level==1 then
--print("Rise delta: "..delta)
if last_valid_rise==0 then
last_valid_rise=when
return
end
if delta>valid_newmin_min and delta<valid_newmin_max then
parse_data(dcfdata)
dcfdata=""
last_valid_rise=when
return
end
if delta>valid_rise_min and delta<valid_rise_max then
--print("valid")
last_valid_rise=when
if gps_valid==true then
gps_sec=gps_sec+1
if gps_sec<60 then
send_gpsstring()
if adc.read(0)>min_ambient_light then
gpio.write(led_pin,gpio.LOW)
end
else
gps_valid=false
gpio.write(led_pin,gpio.HIGH)
end
end
return
end
if delta>valid_timeout then
--print("Valid timeout")
last_valid_rise=0
return
end
end
if level==0 then
--print("Pulse length: "..delta)
if delta>valid_0_min and delta<valid_0_max then
--print("0")
dcfdata=dcfdata.."0"
gpio.write(led_pin,gpio.HIGH)
end
if delta>valid_1_min and delta<valid_1_max then
--print("1")
dcfdata=dcfdata.."1"
gpio.write(led_pin,gpio.HIGH)
end
if string.len(dcfdata)>59 then
dcfdata=string.sub(dcfdata,2)
end
end
end
function irq_dummy(level,when,count)
--print("Dummy IRQ handler")
return
end
poll_prev_value=0
poll_value_count={}
poll_value_count[0]=0
poll_value_count[1]=0
poll_clean=0
function poll_handler()
local value
value=gpio.read(dcf77_pin)
if value==nil then
poll_timer:start()
end
if value==1 and poll_prev_value==0 then
--print(poll_value_count[0],poll_value_count[1])
if poll_value_count[0]>70 and poll_value_count[0]<100 and poll_value_count[1]>6 and poll_value_count[1]<30 then
poll_clean=poll_clean+1
--print(poll_clean.." clean pulses")
if poll_clean>10 then
poll_clean=0
--print("Clean signal, swithcing to IRQ handling")
start_irq()
return
end
else
poll_clean=0
end
poll_value_count[0]=0
poll_value_count[1]=0
end
poll_prev_value=value
poll_value_count[value]=poll_value_count[value]+1
poll_timer:start()
end
poll_timer=tmr.create()
poll_timer:register(10, tmr.ALARM_SEMI,poll_handler)
function start_poll()
gpio.write(led_pin,gpio.HIGH)
gpio.trig(dcf77_pin,"none",irq_dummy)
gpio.mode(dcf77_pin,gpio.INPUT)
poll_timer:start()
end
function start_irq()
gpio.mode(led_pin,gpio.OUTPUT)
gpio.write(led_pin,gpio.HIGH)
gps_valid=false
gpio.trig(dcf77_pin,"both",irq_handler)
gpio.mode(dcf77_pin,gpio.INT)
end
function start_dcf77()
start_irq()
end
function stop_dcf77()
gpio.mode(dcf77_pin,gpio.INPUT)
poll_timer:stop()
gpio.write(led_pin,gpio.HIGH)
end
|