3-phase PV router
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1
11
12#ifndef UTILS_H
13#define UTILS_H
14
15#include <Arduino.h>
16#include <ArduinoJson.h>
17
18#include "FastDivision.h"
19
20#include "calibration.h"
21#include "constants.h"
22#include "dualtariff.h"
23#include "processing.h"
24#include "shared_var.h"
25#include "teleinfo.h"
26
27#include "utils_rf.h"
28#include "utils_temp.h"
29
30#include "version.h"
31
47inline void printConfiguration()
48{
49#ifndef PROJECT_PATH
50#define PROJECT_PATH (__FILE__)
51#endif
52
53#ifndef BRANCH_NAME
54#define BRANCH_NAME ("N/A")
55#endif
56#ifndef COMMIT_HASH
57#define COMMIT_HASH ("N/A")
58#endif
59
60 DBUGLN();
61 DBUGLN();
62 DBUGLN(F("----------------------------------"));
63 DBUG(F("Sketch ID: "));
65
66 DBUG(F("From branch '"));
67 DBUG(F(BRANCH_NAME));
68 DBUG(F("', commit "));
70
71 DBUG(F("Build on "));
72#ifdef CURRENT_TIME
74#else
75 DBUG(F(__DATE__));
76 DBUG(F(" "));
77 DBUGLN(F(__TIME__));
78#endif
79 DBUGLN(F("ADC mode: free-running"));
80
81 DBUGLN(F("Electrical settings"));
82 for (uint8_t phase = 0; phase < NO_OF_PHASES; ++phase)
83 {
84 DBUG(F("\tf_powerCal for L"));
85 DBUG(phase + 1);
86 DBUG(F(" = "));
87 DBUGLN(f_powerCal[phase], 6);
88
89 DBUG(F("\tf_voltageCal, for Vrms_L"));
90 DBUG(phase + 1);
91 DBUG(F(" = "));
92 DBUGLN(f_voltageCal[phase], 5);
93 }
94
95 DBUG(F("\tf_phaseCal for all phases = "));
97
98 DBUG(F("\tExport rate (Watts) = "));
100
101 DBUG(F("\tzero-crossing persistence (sample sets) = "));
103
105
106 DBUG(F("Temperature capability "));
107 if constexpr (TEMP_SENSOR_PRESENT)
108 {
109 DBUGLN(F("is present"));
110 }
111 else
112 {
113 DBUGLN(F("is NOT present"));
114 }
115
116 DBUG(F("Dual-tariff capability "));
117 if constexpr (DUAL_TARIFF)
118 {
119 DBUGLN(F("is present"));
121 }
122 else
123 {
124 DBUGLN(F("is NOT present"));
125 }
126
127 DBUG(F("Load rotation feature "));
128 if constexpr (PRIORITY_ROTATION != RotationModes::OFF)
129 {
130 DBUGLN(F("is present"));
131 }
132 else
133 {
134 DBUGLN(F("is NOT present"));
135 }
136
137 DBUG(F("Relay diversion feature "));
138 if constexpr (RELAY_DIVERSION)
139 {
140 DBUGLN(F("is present"));
141
142 relays.printConfiguration();
143 }
144 else
145 {
146 DBUGLN(F("is NOT present"));
147 }
148
149 DBUG(F("RF capability "));
150#ifdef RF_PRESENT
151 DBUG(F("IS present, Freq = "));
152 if (FREQ == RF12_433MHZ)
153 DBUGLN(F("433 MHz"));
154 else if (FREQ == RF12_868MHZ)
155 DBUGLN(F("868 MHz"));
156 rf12_initialize(nodeID, FREQ, networkGroup); // initialize RF
157#else
158 DBUGLN(F("is NOT present"));
159#endif
160
161 DBUG(F("Datalogging capability "));
163 {
164 DBUGLN(F("in Human-readable format"));
165 }
166 else if constexpr (SERIAL_OUTPUT_TYPE == SerialOutputType::IoT)
167 {
168 DBUGLN(F("in IoT format"));
169 }
170 else if constexpr (SERIAL_OUTPUT_TYPE == SerialOutputType::JSON)
171 {
172 DBUGLN(F("in JSON format"));
173 }
174 else
175 {
176 DBUGLN(F("is NOT present"));
177 }
178}
179
196inline void printForJSON(const bool bOffPeak)
197{
198 ArduinoJson::StaticJsonDocument< 256 > doc;
199
200 // Total mean power over a data logging period
201 doc["P"] = tx_data.power;
202
203 if constexpr (RELAY_DIVERSION)
204 {
205 doc["R"] = relays.get_average();
206 }
207
208 if constexpr (NO_OF_PHASES == 3)
209 {
210 // Mean power for each phase over a data logging period
211 doc["P1"] = tx_data.power_L[0];
212 doc["P2"] = tx_data.power_L[1];
213 doc["P3"] = tx_data.power_L[2];
214 }
215 else if constexpr (NO_OF_PHASES == 2)
216 {
217 // Mean power for each phase over a data logging period
218 doc["P1"] = tx_data.power_L[0];
219 doc["P2"] = tx_data.power_L[1];
220 }
221 else
222 static_assert(NO_OF_PHASES != 1, "Unsupported number of phases");
223
224 // Mean power for each load over a data logging period (in %)
225 //for (idx = 0; idx < NO_OF_DUMPLOADS; ++idx)
226 //{
227 // Serial.print(F(",L"));
228 // Serial.print(idx + 1);
229 // Serial.print(F(":"));
230 // Serial.print(copyOf_countLoadON[idx] * 100 * invDATALOG_PERIOD_IN_MAINS_CYCLES);
231 //}
232
233 if constexpr (TEMP_SENSOR_PRESENT)
234 { // Current temperature
235 for (uint8_t idx = 0; idx < temperatureSensing.get_size(); ++idx)
236 {
237 if ((OUTOFRANGE_TEMPERATURE == tx_data.temperature_x100[idx])
238 || (DEVICE_DISCONNECTED_RAW == tx_data.temperature_x100[idx]))
239 {
240 continue;
241 }
242
243 doc[String("T") + (idx + 1)] = (float)tx_data.temperature_x100[idx] * 0.01F;
244 }
245 }
246
247 if constexpr (DUAL_TARIFF)
248 {
249 // Current tariff
250 doc["TA"] = bOffPeak ? "low" : "high";
251 }
252
253 serializeJson(doc, Serial);
254 Serial.println();
255}
256
271{
272 uint8_t phase{ 0 };
273
275 Serial.print(F(", P:"));
276 Serial.print(tx_data.power);
277
278 if constexpr (RELAY_DIVERSION)
279 {
280 Serial.print(F("/"));
281 Serial.print(relays.get_average());
282 }
283
284 for (phase = 0; phase < NO_OF_PHASES; ++phase)
285 {
286 Serial.print(F(", P"));
287 Serial.print(phase + 1);
288 Serial.print(F(":"));
289 Serial.print(tx_data.power_L[phase]);
290 }
291 for (phase = 0; phase < NO_OF_PHASES; ++phase)
292 {
293 Serial.print(F(", V"));
294 Serial.print(phase + 1);
295 Serial.print(F(":"));
296 Serial.print((float)tx_data.Vrms_L_x100[phase] * 0.01F);
297 }
298
299 if constexpr (TEMP_SENSOR_PRESENT)
300 {
301 for (uint8_t idx = 0; idx < temperatureSensing.get_size(); ++idx)
302 {
303 if ((OUTOFRANGE_TEMPERATURE == tx_data.temperature_x100[idx])
304 || (DEVICE_DISCONNECTED_RAW == tx_data.temperature_x100[idx]))
305 {
306 continue;
307 }
308
309 Serial.print(F(", T"));
310 Serial.print(idx + 1);
311 Serial.print(F(":"));
312 Serial.print((float)tx_data.temperature_x100[idx] * 0.01F);
313 }
314 }
315
316 Serial.print(F(", (minSampleSets/MC "));
318 Serial.print(F(", #ofSampleSets "));
320#ifndef DUAL_TARIFF
321 if constexpr (PRIORITY_ROTATION != RotationModes::OFF)
322 {
323 Serial.print(F(", NoED "));
325 }
326#endif // DUAL_TARIFF
327 Serial.println(F(")"));
328}
329
354{
355 static TeleInfo teleInfo;
356 uint8_t idx{ 0 };
357
358 teleInfo.startFrame(); // Start a new telemetry frame
359
360 teleInfo.send("P", tx_data.power); // Send power grid data
361
362 if constexpr (RELAY_DIVERSION)
363 {
364 teleInfo.send("R", static_cast< int16_t >(relays.get_average())); // Send relay average if diversion is enabled
365
366 idx = 0;
367 do
368 {
369 teleInfo.send("R", relays.get_relay(idx).isRelayON()); // Send diverted energy count for each relay
370 } while (++idx < relays.get_size());
371 }
372
373 idx = 0;
374 do
375 {
376 teleInfo.send("V", tx_data.Vrms_L_x100[idx], idx + 1); // Send voltage for each phase
377 } while (++idx < NO_OF_PHASES);
378
379 idx = 0;
380 do
381 {
382 teleInfo.send("D", Shared::copyOf_countLoadON[idx] * 100 * invDATALOG_PERIOD_IN_MAINS_CYCLES, idx + 1); // Send load ON count for each load
383 } while (++idx < NO_OF_DUMPLOADS);
384
385 if constexpr (TEMP_SENSOR_PRESENT)
386 {
387 for (uint8_t idx = 0; idx < temperatureSensing.get_size(); ++idx)
388 {
389 if ((OUTOFRANGE_TEMPERATURE == tx_data.temperature_x100[idx])
390 || (DEVICE_DISCONNECTED_RAW == tx_data.temperature_x100[idx]))
391 {
392 continue; // Skip invalid temperature readings
393 }
394 teleInfo.send("T", tx_data.temperature_x100[idx], idx + 1); // Send temperature
395 }
396 }
397
398 teleInfo.send("N", static_cast< int16_t >(Shared::absenceOfDivertedEnergyCountInSeconds)); // Send absence of diverted energy count for 50Hz
399
402
403 teleInfo.endFrame(); // Finalize and send the telemetry frame
404}
405
423inline void sendResults(bool bOffPeak)
424{
425 static bool startup{ true };
426
427 if (startup)
428 {
429 startup = false;
430 return; // reject the first datalogging which is incomplete !
431 }
432
433#ifdef RF_PRESENT
434 send_rf_data(); // *SEND RF DATA*
435#endif
436
438 {
440 }
441 else if constexpr (SERIAL_OUTPUT_TYPE == SerialOutputType::IoT)
442 {
444 }
445 else if constexpr (SERIAL_OUTPUT_TYPE == SerialOutputType::JSON)
446 {
447 printForJSON(bOffPeak);
448 }
449}
450
464inline void logLoadPriorities()
465{
466#ifdef ENABLE_DEBUG
467
468 DBUGLN(F("Load Priorities: "));
469 for (const auto& loadPrioAndState : loadPrioritiesAndState)
470 {
471 DBUG(F("\tload "));
472 DBUGLN(loadPrioAndState);
473 }
474
475#endif
476}
477
489inline int freeRam()
490{
491 extern int __heap_start, *__brkval;
492 int v;
493 return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
494}
495
496#endif // UTILS_H
Calibration values definition.
constexpr float f_phaseCal
Definition calibration.h:46
constexpr float f_voltageCal[NO_OF_PHASES]
Definition calibration.h:51
constexpr float f_powerCal[NO_OF_PHASES]
Definition calibration.h:34
A class for managing and sending telemetry information in a structured frame format.
Definition teleinfo.h:155
void send(const char *tag, int16_t value, uint8_t index=0)
Sends a telemetry value as an integer.
Definition teleinfo.h:229
#define FREQ
Definition config.h:116
constexpr bool RELAY_DIVERSION
Definition config.h:40
constexpr int networkGroup
Definition config.h:119
constexpr TemperatureSensing temperatureSensing
Definition config.h:98
constexpr RelayEngine relays
Definition config.h:91
constexpr int nodeID
Definition config.h:118
constexpr bool DUAL_TARIFF
Definition config.h:41
constexpr SerialOutputType SERIAL_OUTPUT_TYPE
Definition config.h:27
constexpr uint8_t NO_OF_DUMPLOADS
Definition config.h:32
constexpr bool TEMP_SENSOR_PRESENT
Definition config.h:42
constexpr int16_t REQUIRED_EXPORT_IN_WATTS
constexpr float invDATALOG_PERIOD_IN_MAINS_CYCLES
constexpr float invSUPPLY_FREQUENCY
constexpr uint8_t NO_OF_PHASES
Some constants.
constexpr int16_t DEVICE_DISCONNECTED_RAW
Definition constants.h:27
constexpr int16_t OUTOFRANGE_TEMPERATURE
Definition constants.h:22
#define DBUGLN(...)
Definition debug.h:97
#define DBUG(...)
Definition debug.h:96
Classes/types needed for dual-tariff support.
void printDualTariffConfiguration()
Print the settings for off-peak period.
Definition dualtariff.h:71
void sendResults(bool bOffPeak)
Prints or sends telemetry data logs based on the selected output format.
Definition utils.h:423
void logLoadPriorities()
Prints the load priorities to the Serial output.
Definition utils.h:464
void printConfiguration()
Print the configuration during startup.
Definition utils.h:47
void printForJSON(const bool bOffPeak)
Write telemetry data to Serial in JSON format.
Definition utils.h:196
void printForSerialText()
Prints data logs to the Serial output in text format.
Definition utils.h:270
volatile float copyOf_energyInBucket_main
Definition shared_var.h:24
volatile uint16_t copyOf_sampleSetsDuringThisDatalogPeriod
Definition shared_var.h:26
volatile uint8_t copyOf_lowestNoOfSampleSetsPerMainsCycle
Definition shared_var.h:25
volatile uint16_t copyOf_countLoadON[NO_OF_DUMPLOADS]
Definition shared_var.h:27
volatile uint16_t absenceOfDivertedEnergyCountInSeconds
Definition shared_var.h:17
void printParamsForSelectedOutputMode()
Print the settings used for the selected output mode.
Public functions/variables of processing engine.
constexpr uint8_t PERSISTENCE_FOR_POLARITY_CHANGE
Definition processing.h:24
uint8_t loadPrioritiesAndState[NO_OF_DUMPLOADS]
Definition processing.h:22
PayloadTx_struct< NO_OF_PHASES > tx_data
Definition processing.h:32
Manages telemetry data and frame formatting.
void sendTelemetryData()
Sends telemetry data using the TeleInfo class.
Definition utils.h:353
int freeRam()
Get the available RAM during setup.
Definition utils.h:489
Some utility functions for the RF chip.
void send_rf_data()
Send the logging data through RF.
Definition utils_rf.h:26
Provides utilities for managing temperature sensors.
#define BRANCH_NAME
Definition version.h:6
#define COMMIT_HASH
Definition version.h:7
#define CURRENT_TIME
Definition version.h:5
#define PROJECT_PATH
Definition version.h:4