@@ -7,187 +7,229 @@ function main()
77 local err = can .init (BAUD_RATE , can_handler )
88 if err and err ~= 0 then
99 enapter .log (" CAN failed: " .. err .. " " .. can .err_to_str (err ), " error" , true )
10+ scheduler .add (5000 , function ()
11+ enapter .send_telemetry ({ status = ' error' , alerts = {' can_init_failed' } })
12+ end )
13+ else
14+ scheduler .add (30000 , send_properties )
15+ scheduler .add (1000 , send_telemetry )
1016 end
11-
12- scheduler .add (30000 , properties )
13- scheduler .add (1000 , metrics )
1417end
1518
16- function properties ()
19+ function send_properties ()
1720 enapter .send_properties ({ vendor = " PylonTech" , model = " US2000" })
1821end
1922
20- function metrics ()
21- if next (telemetry ) ~= nil then
23+ -- Holds the number of `send_telemetry` that had an empty `telemetry`
24+ local missed_telemetry_count = 0
25+
26+ function send_telemetry ()
27+ if telemetry [1 ] ~= nil then
2228 enapter .send_telemetry (telemetry )
29+ -- Cleanup telemetry and let it be refilled by `can_handler`
30+ telemetry = {}
31+ missed_telemetry_count = 0
32+ else
33+ missed_telemetry_count = missed_telemetry_count + 1
34+ if missed_telemetry_count > 5 then
35+ enapter .send_telemetry ({
36+ status = ' read_error' ,
37+ alerts = {' can_fail' }
38+ })
39+ end
2340 end
2441end
2542
2643function can_handler (msg_id , data )
2744 local alerts = {}
2845 local status = " ok"
2946
30- if msg_id == 0x359 then
31- local ok , err = pcall (function ()
32- local byte0 , byte1 , byte2 , byte3 = string.unpack (" <I1<I1<I1<I1" , data )
33-
34- if byte0 & 2 ~= 0 then
35- table.insert (alerts , " cell_or_module_over_voltage" )
36- end
37- if byte0 & 4 ~= 0 then
38- table.insert (alerts , " cell_or_module_under_voltage" )
39- end
40- if byte0 & 8 ~= 0 then
41- table.insert (alerts , " cell_over_temperature" )
42- end
43- if byte0 & 16 ~= 0 then
44- table.insert (alerts , " cell_under_temperature" )
45- end
46- if byte0 & 16 ~= 0 then
47- table.insert (alerts , " discharge_over_current" )
48- end
49- if byte0 == 0 then
50- telemetry [" has_protection1" ] = false
51- else
52- telemetry [" has_protection1" ] = true
53- status = " warning"
54- end
55-
56- if byte1 & 1 ~= 0 then
57- table.insert (alerts , " charge_over_current" )
58- end
59- if byte1 & 8 ~= 0 then
60- table.insert (alerts , " system_error" )
61- end
62- if byte1 == 0 then
63- telemetry [" has_protection2" ] = false
64- else
65- telemetry [" has_protection2" ] = true
66- status = " warning"
67- end
68-
69- if byte2 & 2 ~= 0 then
70- table.insert (alerts , " cell_or_module_high_voltage" )
71- end
72- if byte2 & 4 ~= 0 then
73- table.insert (alerts , " cell_or_module_low_voltage" )
74- end
75- if byte2 & 8 ~= 0 then
76- table.insert (alerts , " cell_high_temperature" )
77- end
78- if byte2 & 16 ~= 0 then
79- table.insert (alerts , " cell_low_temperature" )
80- end
81- if byte2 & 16 ~= 0 then
82- table.insert (alerts , " discharge_high_current" )
83- end
84- if byte2 == 0 then
85- telemetry [" has_alarm1" ] = false
86- else
87- telemetry [" has_alarm1" ] = true
88- status = " warning"
89- end
90-
91- if byte3 & 1 ~= 0 then
92- table.insert (alerts , " charge_high_current" )
93- end
94- if byte3 & 8 ~= 0 then
95- table.insert (alerts , " internal_communication_fail" )
96- end
97- if byte3 == 0 then
98- telemetry [" has_alarm2" ] = false
99- else
100- telemetry [" has_alarm2" ] = true
101- status = " warning"
102- end
103- end )
104- if not ok then
105- enapter .log (" Reading message 0x351 failed: " .. err , " error" )
106- table.insert (alerts , " can_fail" )
107- status = " read_error"
108- end
109- end
110-
11147 if msg_id == 0x351 then
112- local ok , err = pcall (function ()
113- local byte_pair1 , byte_pair2 , byte_pair3 , byte_pair4 = string.unpack (" I2i2i2I2" , data )
114- telemetry [" battery_charge_voltage" ] = byte_pair1 / 10.0
115- telemetry [" charge_current_limit" ] = byte_pair2 / 10.0
116- telemetry [" discharge_current_limit" ] = byte_pair3 / 10.0
117- telemetry [" discharge_voltage" ] = byte_pair4 / 10.0
118- end )
119- if not ok then
120- enapter .log (" Reading message 0x351 failed: " .. err , " error" )
121- table.insert (alerts , " can_fail" )
122- status = " read_error"
123- end
48+ status , alerts = handle_msg_0x351 (data , status , alerts )
12449 end
12550
12651 if msg_id == 0x355 then
127- local ok , err = pcall (function ()
128- local soc , soh = string.unpack (" I2I2" , data )
129- telemetry [" soh" ] = soh
130- telemetry [" soc" ] = soc
131- end )
132- if not ok then
133- enapter .log (" Reading message 0x355 failed: " .. err , " error" )
134- table.insert (alerts , " can_fail" )
135- status = " read_error"
136- end
52+ status , alerts = handle_msg_0x355 (data , status , alerts )
13753 end
13854
13955 if msg_id == 0x356 then
140- local ok , err = pcall (function ()
141- local voltage , current , temp = string.unpack (" I2i2i2" , data )
142- telemetry [" voltage" ] = voltage / 100.0
143- telemetry [" total_current" ] = current / 10.0
144- telemetry [" average_cell_temperature" ] = temp / 10.0
145- end )
146- if not ok then
147- enapter .log (" Reading message 0x356 failed: " .. err , " error" )
148- table.insert (alerts , " can_fail" )
149- status = " read_error"
150- end
56+ status , alerts = handle_msg_0x356 (data , status , alerts )
57+ end
58+
59+ if msg_id == 0x359 then
60+ status , alerts = handle_msg_0x359 (data , status , alerts )
15161 end
15262
15363 if msg_id == 0x35C then
154- local ok , err = pcall (function ()
155- local byte = string.unpack (" <I1" , data )
156- if byte & 8 ~= 0 then
157- telemetry [" request_force_charge" ] = true
158- else
159- telemetry [" request_force_charge" ] = false
160- end
161- if byte & 16 ~= 0 then
162- telemetry [" request_force_charge_2" ] = true
163- else
164- telemetry [" request_force_charge_2" ] = false
165- end
166- if byte & 32 ~= 0 then
167- telemetry [" request_force_charge_1" ] = true
168- else
169- telemetry [" request_force_charge_1" ] = false
170- end
171- if byte & 64 ~= 0 then
172- telemetry [" discharge_enable" ] = true
173- else
174- telemetry [" discharge_enable" ] = false
175- end
176- if byte & 128 ~= 0 then
177- telemetry [" charge_enable" ] = true
178- else
179- telemetry [" charge_enable" ] = false
180- end
181- end )
182- if not ok then
183- enapter .log (" Reading message 0x35C failed: " .. err , " error" )
184- table.insert (alerts , " can_fail" )
185- status = " read_error"
186- end
64+ status , alerts = handle_msg_0x35C (data , status , alerts )
18765 end
18866
18967 telemetry [" status" ] = status
19068 telemetry [" alerts" ] = alerts
19169end
19270
71+ function handle_msg_0x351 (data , status , alerts )
72+ local ok , err = pcall (function ()
73+ local byte_pair1 , byte_pair2 , byte_pair3 , byte_pair4 = string.unpack (" I2i2i2I2" , data )
74+ telemetry [" battery_charge_voltage" ] = byte_pair1 / 10.0
75+ telemetry [" charge_current_limit" ] = byte_pair2 / 10.0
76+ telemetry [" discharge_current_limit" ] = byte_pair3 / 10.0
77+ telemetry [" discharge_voltage" ] = byte_pair4 / 10.0
78+ end )
79+ if not ok then
80+ enapter .log (" Reading message 0x351 failed: " .. err , " error" )
81+ table.insert (alerts , " can_fail" )
82+ status = " read_error"
83+ end
84+ return status , alerts
85+ end
86+
87+ function handle_msg_0x355 (data , status , alerts )
88+ local ok , err = pcall (function ()
89+ local soc , soh = string.unpack (" I2I2" , data )
90+ telemetry [" soh" ] = soh
91+ telemetry [" soc" ] = soc
92+ end )
93+ if not ok then
94+ enapter .log (" Reading message 0x355 failed: " .. err , " error" )
95+ table.insert (alerts , " can_fail" )
96+ status = " read_error"
97+ end
98+ return status , alerts
99+ end
100+
101+ function handle_msg_0x356 (data , status , alerts )
102+ local ok , err = pcall (function ()
103+ local voltage , current , temp = string.unpack (" I2i2i2" , data )
104+ telemetry [" voltage" ] = voltage / 100.0
105+ telemetry [" total_current" ] = current / 10.0
106+ telemetry [" average_cell_temperature" ] = temp / 10.0
107+ end )
108+ if not ok then
109+ enapter .log (" Reading message 0x356 failed: " .. err , " error" )
110+ table.insert (alerts , " can_fail" )
111+ status = " read_error"
112+ end
113+ return status , alerts
114+ end
115+
116+ function handle_msg_0x359 (data , status , alerts )
117+ local ok , err = pcall (function ()
118+ local byte0 , byte1 , byte2 , byte3 = string.unpack (" <I1<I1<I1<I1" , data )
119+
120+ if byte0 & 2 ~= 0 then
121+ table.insert (alerts , " cell_or_module_over_voltage" )
122+ end
123+ if byte0 & 4 ~= 0 then
124+ table.insert (alerts , " cell_or_module_under_voltage" )
125+ end
126+ if byte0 & 8 ~= 0 then
127+ table.insert (alerts , " cell_over_temperature" )
128+ end
129+ if byte0 & 16 ~= 0 then
130+ table.insert (alerts , " cell_under_temperature" )
131+ end
132+ if byte0 & 16 ~= 0 then
133+ table.insert (alerts , " discharge_over_current" )
134+ end
135+ if byte0 == 0 then
136+ telemetry [" has_protection1" ] = false
137+ else
138+ telemetry [" has_protection1" ] = true
139+ status = " warning"
140+ end
141+
142+ if byte1 & 1 ~= 0 then
143+ table.insert (alerts , " charge_over_current" )
144+ end
145+ if byte1 & 8 ~= 0 then
146+ table.insert (alerts , " system_error" )
147+ end
148+ if byte1 == 0 then
149+ telemetry [" has_protection2" ] = false
150+ else
151+ telemetry [" has_protection2" ] = true
152+ status = " warning"
153+ end
154+
155+ if byte2 & 2 ~= 0 then
156+ table.insert (alerts , " cell_or_module_high_voltage" )
157+ end
158+ if byte2 & 4 ~= 0 then
159+ table.insert (alerts , " cell_or_module_low_voltage" )
160+ end
161+ if byte2 & 8 ~= 0 then
162+ table.insert (alerts , " cell_high_temperature" )
163+ end
164+ if byte2 & 16 ~= 0 then
165+ table.insert (alerts , " cell_low_temperature" )
166+ end
167+ if byte2 & 16 ~= 0 then
168+ table.insert (alerts , " discharge_high_current" )
169+ end
170+ if byte2 == 0 then
171+ telemetry [" has_alarm1" ] = false
172+ else
173+ telemetry [" has_alarm1" ] = true
174+ status = " warning"
175+ end
176+
177+ if byte3 & 1 ~= 0 then
178+ table.insert (alerts , " charge_high_current" )
179+ end
180+ if byte3 & 8 ~= 0 then
181+ table.insert (alerts , " internal_communication_fail" )
182+ end
183+ if byte3 == 0 then
184+ telemetry [" has_alarm2" ] = false
185+ else
186+ telemetry [" has_alarm2" ] = true
187+ status = " warning"
188+ end
189+ end )
190+ if not ok then
191+ enapter .log (" Reading message 0x351 failed: " .. err , " error" )
192+ table.insert (alerts , " can_fail" )
193+ status = " read_error"
194+ end
195+ return status , alerts
196+ end
197+
198+ function handle_msg_0x35C (data , status , alerts )
199+ local ok , err = pcall (function ()
200+ local byte = string.unpack (" <I1" , data )
201+ if byte & 8 ~= 0 then
202+ telemetry [" request_force_charge" ] = true
203+ else
204+ telemetry [" request_force_charge" ] = false
205+ end
206+ if byte & 16 ~= 0 then
207+ telemetry [" request_force_charge_2" ] = true
208+ else
209+ telemetry [" request_force_charge_2" ] = false
210+ end
211+ if byte & 32 ~= 0 then
212+ telemetry [" request_force_charge_1" ] = true
213+ else
214+ telemetry [" request_force_charge_1" ] = false
215+ end
216+ if byte & 64 ~= 0 then
217+ telemetry [" discharge_enable" ] = true
218+ else
219+ telemetry [" discharge_enable" ] = false
220+ end
221+ if byte & 128 ~= 0 then
222+ telemetry [" charge_enable" ] = true
223+ else
224+ telemetry [" charge_enable" ] = false
225+ end
226+ end )
227+ if not ok then
228+ enapter .log (" Reading message 0x35C failed: " .. err , " error" )
229+ table.insert (alerts , " can_fail" )
230+ status = " read_error"
231+ end
232+ return status , alerts
233+ end
234+
193235main ()
0 commit comments