-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
Copy pathxnrg_07_ade7953.ino
903 lines (817 loc) · 43.7 KB
/
xnrg_07_ade7953.ino
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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
/*
xnrg_07_ade7953.ino - ADE7953 energy sensor support for Tasmota
Copyright (C) 2021 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(ESP32) && defined(USE_SPI)
#define USE_ESP32_SPI
#endif
#if defined(USE_I2C) || defined(USE_ESP32_SPI)
#ifdef USE_ENERGY_SENSOR
#ifdef USE_ADE7953
/*********************************************************************************************\
* ADE7953 - Energy used in Shelly 2.5 (model 1), EM (model 2), Plus 2PM and 2PM Gen3 (model 3), Pro 1PM (model 4), Pro 2PM (model 5) and Pro 4PM (model 6)
*
* {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18}
* {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,640,3457,608,224,8832,1],"FLAG":0,"BASE":18}
* {"NAME":"Shelly Plus 2PM PCB v0.1.5","GPIO":[320,0,192,0,0,0,1,1,225,224,0,0,0,0,193,0,0,0,0,0,0,608,3840,32,0,0,0,0,0,640,0,0,3458,4736,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,32000,40000,3350"}
* {"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,32,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3458,0,0,0,0,0,9472,0,4736,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}
* {"NAME":"Shelly Pro 1PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3459,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 2PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,9569,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3460,0,0,32,4736,4737,160,161],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;AdcParam2 2,5600,4700,3350"}
* {"NAME":"Shelly Pro 4PM","GPIO":[0,6210,0,6214,9568,0,0,0,0,0,9569,0,768,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,736,704,3461,0,4736,0,0,672],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"}
* {"NAME":"Shelly 2PM Gen3","GPIO":[9472,3458,576,225,4736,224,640,608,1,1,193,0,0,0,0,0,0,0,192,32,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio4 10000,10000,4000"}
*
* Based on datasheet from https://www.analog.com/en/products/ade7953.html
*
* Model differences:
* Function Model1 Model2 Model3 Model4 Model5 Model6 Remark
* ------------------------------ ------- ------- ------- ------ ------ ------ -------------------------------------------------
* Shelly 2.5 EM Plus2PM Pro1PM Pro2PM Pro4PM Shelly hardware
* 2PMGen3
* Processor ESP8266 ESP8266 ESP32 ESP32 ESP32 ESP32
* ESP32C3 Shelly Gen3
* Interface I2C I2C I2C SPI SPI SPI Interface type used
* Number of inputs 2 2 2 1 2 4 Count of ADE9753 inputs used
* Number of ADE9753 chips 1 1 1 1 2 2 Count of ADE9753 chips
* ADE9753 IRQ 1 2 3 4 5 6 Index defines model number
* Current measurement device shunt CT shunt shunt shunt shunt CT = Current Transformer
* Common voltage Yes Yes Yes No No Yes Show common voltage in GUI/JSON
* Common frequency Yes Yes Yes No No Yes Show common frequency in GUI/JSON
* Swapped channel A/B Yes No No No No No Defined by hardware design - Fixed by Tasmota
* Support Export Active No Yes No No No No Only EM supports correct negative value detection
* Show negative (reactive) power No Yes No No No No Only EM supports correct negative value detection
* Default phase calibration 0 200 0 0 0 0 CT needs different phase calibration than shunts
* Default reset pin on ESP8266 - 16 - - - - Legacy support. Replaced by GPIO ADE7953RST
*
* I2C Address: 0x38
*********************************************************************************************
* Optionally allowing users to tweak calibration registers:
* - In addition to possible rules add a rule containing the calib.dat string like:
* - rule3 on file#calib.dat do {"angles":{"angle0":180,"angle1":176}} endon
* - rule3 on file#calib.dat do {"rms":{"current_a":4194303,"current_b":4194303,"voltage":1613194},"angles":{"angle0":200,"angle1":200},"powers":{"totactive":{"a":2723574,"b":2723574},"apparent":{"a":2723574,"b":2723574},"reactive":{"a":2723574,"b":2723574}}} endon
* - Restart Tasmota and obeserve that the results seem calibrated as Tasmota now uses the information from calib.dat
* To restore standard calibration using commands like VoltSet remove above entry from rule3
\*********************************************************************************************/
#define XNRG_07 7
#define XI2C_07 7 // See I2CDEVICES.md
#define ADE7953_ADDR 0x38
/*********************************************************************************************/
#define ADE7953_ACCU_ENERGY // Use accumulating energy instead of instant power
//#define ADE7953_DUMP_REGS
#define ADE7953_PREF 1540 // 4194304 / (1540 / 1000) = 2723574 (= WGAIN, VAGAIN and VARGAIN)
#define ADE7953_UREF 26000 // 4194304 / (26000 / 10000) = 1613194 (= VGAIN)
#define ADE7953_IREF 10000 // 4194304 / (10000 / 10000) = 4194303 (= IGAIN, needs to be different than 4194304 in order to use calib.dat)
#define ADE7953_NO_LOAD_THRESHOLD 29196 // According to ADE7953 datasheet the default threshold for no load detection is 58,393 use half this value to measure lower (5w) powers.
#define ADE7953_NO_LOAD_ENABLE 0 // Set DISNOLOAD register to 0 to enable No-load detection
#define ADE7953_NO_LOAD_DISABLE 7 // Set DISNOLOAD register to 7 to disable No-load detection
// Default calibration parameters can be overridden by a rule as documented above.
#define ADE7953_GAIN_DEFAULT 4194304 // = 0x400000 range 2097152 (min) to 6291456 (max)
#define ADE7953_PHCAL_DEFAULT 0 // = range -383 to 383 - Default phase calibration for Shunts
#define ADE7953_PHCAL_DEFAULT_CT 200 // = range -383 to 383 - Default phase calibration for Current Transformers (Shelly EM)
#define ADE7953_MAX_CHANNEL 4
enum Ade7953Models { ADE7953_SHELLY_25, ADE7953_SHELLY_EM, ADE7953_SHELLY_PLUS_2PM, ADE7953_SHELLY_PRO_1PM, ADE7953_SHELLY_PRO_2PM, ADE7953_SHELLY_PRO_4PM };
enum Ade7953_8BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_SAGCYC = 0x000, // 0x000 R/W 8 U 0x00 Sag line cycles
ADE7953_DISNOLOAD, // 0x001 R/W 8 U 0x00 No-load detection disable (see Table 16)
ADE7953_RESERVED_0X002, // 0x002
ADE7953_RESERVED_0X003, // 0x003
ADE7953_LCYCMODE, // 0x004 R/W 8 U 0x40 Line cycle accumulation mode configuration (see Table 17)
ADE7953_RESERVED_0X005, // 0x005
ADE7953_RESERVED_0X006, // 0x006
ADE7953_PGA_V, // 0x007 R/W 8 U 0x00 Voltage channel gain configuration (Bits[2:0])
ADE7953_PGA_IA, // 0x008 R/W 8 U 0x00 Current Channel A gain configuration (Bits[2:0])
ADE7953_PGA_IB // 0x009 R/W 8 U 0x00 Current Channel B gain configuration (Bits[2:0])
};
enum Ade7953_16BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_ZXTOUT = 0x100, // 0x100 R/W 16 U 0xFFFF Zero-crossing timeout
ADE7953_LINECYC, // 0x101 R/W 16 U 0x0000 Number of half line cycles for line cycle energy accumulation mode
ADE7953_CONFIG, // 0x102 R/W 16 U 0x8004 Configuration register (see Table 18)
ADE7953_CF1DEN, // 0x103 R/W 16 U 0x003F CF1 frequency divider denominator. When modifying this register, two sequential write operations must be performed to ensure that the write is successful.
ADE7953_CF2DEN, // 0x104 R/W 16 U 0x003F CF2 frequency divider denominator. When modifying this register, two sequential write operations must be performed to ensure that the write is successful.
ADE7953_RESERVED_0X105, // 0x105
ADE7953_RESERVED_0X106, // 0x106
ADE7953_CFMODE, // 0x107 R/W 16 U 0x0300 CF output selection (see Table 19)
ADE7943_PHCALA, // 0x108 R/W 16 S 0x0000 Phase calibration register (Current Channel A). This register is in sign magnitude format.
ADE7943_PHCALB, // 0x109 R/W 16 S 0x0000 Phase calibration register (Current Channel B). This register is in sign magnitude format.
ADE7943_PFA, // 0x10A R 16 S 0x0000 Power factor (Current Channel A)
ADE7943_PFB, // 0x10B R 16 S 0x0000 Power factor (Current Channel B)
ADE7943_ANGLE_A, // 0x10C R 16 S 0x0000 Angle between the voltage input and the Current Channel A input
ADE7943_ANGLE_B, // 0x10D R 16 S 0x0000 Angle between the voltage input and the Current Channel B input
ADE7943_Period, // 0x10E R 16 U 0x0000 Period register
ADE7953_RESERVED_0X120 = 0x120 // 0x120 This register should be set to 30h to meet the performance specified in Table 1. To modify this register, it must be unlocked by setting Register Address 0xFE to 0xAD immediately prior.
};
enum Ade7953_32BitRegisters {
// Register Name Addres R/W Bt Ty Default Description
// ---------------------------- ------ --- -- -- ---------- --------------------------------------------------------------------
ADE7953_ACCMODE = 0x301, // 0x301 R/W 24 U 0x000000 Accumulation mode (see Table 21)
ADE7953_AP_NOLOAD = 0x303, // 0x303 R/W 24 U 0x00E419 No load threshold for actual power
ADE7953_VAR_NOLOAD, // 0x304 R/W 24 U 0x00E419 No load threshold for reactive power
ADE7953_VA_NOLOAD, // 0x305 R/W 24 U 0x000000 No load threshold for appearant power
ADE7953_AVA = 0x310, // 0x310 R 24 S 0x000000 Instantaneous apparent power (Current Channel A)
ADE7953_BVA, // 0x311 R 24 S 0x000000 Instantaneous apparent power (Current Channel B)
ADE7953_AWATT, // 0x312 R 24 S 0x000000 Instantaneous active power (Current Channel A)
ADE7953_BWATT, // 0x313 R 24 S 0x000000 Instantaneous active power (Current Channel B)
ADE7953_AVAR, // 0x314 R 24 S 0x000000 Instantaneous reactive power (Current Channel A)
ADE7953_BVAR, // 0x315 R 24 S 0x000000 Instantaneous reactive power (Current Channel B)
ADE7953_IA, // 0x316 R 24 S 0x000000 Instantaneous current (Current Channel A)
ADE7953_IB, // 0x317 R 24 S 0x000000 Instantaneous current (Current Channel B)
ADE7953_V, // 0x318 R 24 S 0x000000 Instantaneous voltage (voltage channel)
ADE7953_RESERVED_0X319, // 0x319
ADE7953_IRMSA, // 0x31A R 24 U 0x000000 IRMS register (Current Channel A)
ADE7953_IRMSB, // 0x31B R 24 U 0x000000 IRMS register (Current Channel B)
ADE7953_VRMS, // 0x31C R 24 U 0x000000 VRMS register
ADE7953_RESERVED_0X31D, // 0x31D
ADE7953_AENERGYA, // 0x31E R 24 S 0x000000 Active energy (Current Channel A)
ADE7953_AENERGYB, // 0x31F R 24 S 0x000000 Active energy (Current Channel B)
ADE7953_RENERGYA, // 0x320 R 24 S 0x000000 Reactive energy (Current Channel A)
ADE7953_RENERGYB, // 0x321 R 24 S 0x000000 Reactive energy (Current Channel B)
ADE7953_APENERGYA, // 0x322 R 24 S 0x000000 Apparent energy (Current Channel A)
ADE7953_APENERGYB, // 0x323 R 24 S 0x000000 Apparent energy (Current Channel B)
ADE7953_OVLVL, // 0x324 R/W 24 U 0xFFFFFF Overvoltage level
ADE7953_OILVL, // 0x325 R/W 24 U 0xFFFFFF Overcurrent level
ADE7953_VPEAK, // 0x326 R 24 U 0x000000 Voltage channel peak
ADE7953_RSTVPEAK, // 0x327 R 24 U 0x000000 Read voltage peak with reset
ADE7953_IAPEAK, // 0x328 R 24 U 0x000000 Current Channel A peak
ADE7953_RSTIAPEAK, // 0x329 R 24 U 0x000000 Read Current Channel A peak with reset
ADE7953_IBPEAK, // 0x32A R 24 U 0x000000 Current Channel B peak
ADE7953_RSTIBPEAK, // 0x32B R 24 U 0x000000 Read Current Channel B peak with reset
ADE7953_IRQENA, // 0x32C R/W 24 U 0x100000 Interrupt enable (Current Channel A, see Table 22)
ADE7953_IRQSTATA, // 0x32D R 24 U 0x000000 Interrupt status (Current Channel A, see Table 23)
ADE7953_RSTIRQSTATA, // 0x32E R 24 U 0x000000 Reset interrupt status (Current Channel A)
ADE7953_IRQENB, // 0x32F R/W 24 U 0x000000 Interrupt enable (Current Channel B, see Table 24)
ADE7953_IRQSTATB, // 0x330 R 24 U 0x000000 Interrupt status (Current Channel B, see Table 25)
ADE7953_RSTIRQSTATB, // 0x331 R 24 U 0x000000 Reset interrupt status (Current Channel B)
ADE7953_CRC = 0x37F, // 0x37F R 32 U 0xFFFFFFFF Checksum
ADE7953_AIGAIN, // 0x380 R/W 24 U 0x400000 Current channel gain (Current Channel A)
ADE7953_AVGAIN, // 0x381 R/W 24 U 0x400000 Voltage channel gain
ADE7953_AWGAIN, // 0x382 R/W 24 U 0x400000 Active power gain (Current Channel A)
ADE7953_AVARGAIN, // 0x383 R/W 24 U 0x400000 Reactive power gain (Current Channel A)
ADE7953_AVAGAIN, // 0x384 R/W 24 U 0x400000 Apparent power gain (Current Channel A)
ADE7953_RESERVED_0X385, // 0x385
ADE7953_AIRMSOS, // 0x386 R/W 24 S 0x000000 IRMS offset (Current Channel A)
ADE7953_RESERVED_0X387, // 0x387
ADE7953_VRMSOS, // 0x388 R/W 24 S 0x000000 VRMS offset
ADE7953_AWATTOS, // 0x389 R/W 24 S 0x000000 Active power offset correction (Current Channel A)
ADE7953_AVAROS, // 0x38A R/W 24 S 0x000000 Reactive power offset correction (Current Channel A)
ADE7953_AVAOS, // 0x38B R/W 24 S 0x000000 Apparent power offset correction (Current Channel A)
ADE7953_BIGAIN, // 0x38C R/W 24 U 0x400000 Current channel gain (Current Channel B)
ADE7953_BVGAIN, // 0x38D R/W 24 U 0x400000 This register should not be modified.
ADE7953_BWGAIN, // 0x38E R/W 24 U 0x400000 Active power gain (Current Channel B)
ADE7953_BVARGAIN, // 0x38F R/W 24 U 0x400000 Reactive power gain (Current Channel B)
ADE7953_BVAGAIN, // 0x390 R/W 24 U 0x400000 Apparent power gain (Current Channel B)
ADE7953_RESERVED_0X391, // 0x391
ADE7953_BIRMSOS, // 0x392 R/W 24 S 0x000000 IRMS offset (Current Channel B)
ADE7953_RESERVED_0X393, // 0x393
ADE7953_RESERVED_0X394, // 0x394
ADE7953_BWATTOS, // 0x395 R/W 24 S 0x000000 Active power offset correction (Current Channel B)
ADE7953_BVAROS, // 0x396 R/W 24 S 0x000000 Reactive power offset correction (Current Channel B)
ADE7953_BVAOS // 0x397 R/W 24 S 0x000000 Apparent power offset correction (Current Channel B)
};
enum Ade7953CalibrationRegisters {
ADE7953_CAL_VGAIN,
ADE7953_CAL_IGAIN,
ADE7953_CAL_WGAIN,
ADE7953_CAL_VAGAIN,
ADE7953_CAL_VARGAIN,
ADE7943_CAL_PHCAL
};
const uint8_t ADE7953_CALIBREGS = 6;
const uint16_t Ade7953CalibRegs[2][ADE7953_CALIBREGS] {
{ ADE7953_AVGAIN, ADE7953_AIGAIN, ADE7953_AWGAIN, ADE7953_AVAGAIN, ADE7953_AVARGAIN, ADE7943_PHCALA },
{ ADE7953_BVGAIN, ADE7953_BIGAIN, ADE7953_BWGAIN, ADE7953_BVAGAIN, ADE7953_BVARGAIN, ADE7943_PHCALB }
};
const uint8_t ADE7953_REGISTERS = 6;
const uint16_t Ade7953Registers[2][ADE7953_REGISTERS] {
#ifdef ADE7953_ACCU_ENERGY
{ ADE7953_IRMSA, ADE7953_AENERGYA, ADE7953_APENERGYA, ADE7953_RENERGYA, ADE7953_VRMS, ADE7943_Period },
{ ADE7953_IRMSB, ADE7953_AENERGYB, ADE7953_APENERGYB, ADE7953_RENERGYB, ADE7953_VRMS, ADE7943_Period }
#else // No ADE7953_ACCU_ENERGY
{ ADE7953_IRMSA, ADE7953_AWATT, ADE7953_AVA, ADE7953_AVAR, ADE7953_VRMS, ADE7943_Period },
{ ADE7953_IRMSB, ADE7953_BWATT, ADE7953_BVA, ADE7953_BVAR, ADE7953_VRMS, ADE7943_Period }
#endif // ADE7953_ACCU_ENERGY
};
#ifdef ADE7953_ACCU_ENERGY
const float ADE7953_LSB_PER_WATTSECOND = 2.5;
const float ADE7953_POWER_CORRECTION = 23.41494; // See https://github.com/arendst/Tasmota/pull/16941
#else // No ADE7953_ACCU_ENERGY
const float ADE7953_LSB_PER_WATTSECOND = 44;
#endif // ADE7953_ACCU_ENERGY
typedef struct {
uint32_t voltage_rms;
uint32_t current_rms;
uint32_t active_power;
int32_t calib_data[ADE7953_CALIBREGS];
} tAde7953Channel;
struct Ade7953 {
uint32_t last_update;
uint32_t voltage_rms[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t current_rms[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t active_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
int32_t calib_data[ADE7953_MAX_CHANNEL][ADE7953_CALIBREGS];
uint8_t init_step = 0;
uint8_t model = 0; // 0 = Shelly 2.5, 1 = Shelly EM, 2 = Shelly Plus 2PM, 3 = Shelly Pro 1PM, 4 = Shelly Pro 2PM, 5 = Shelly Pro 4PM
uint8_t cs_index;
#ifdef USE_ESP32_SPI
int8_t pin_cs[ADE7953_MAX_CHANNEL / 2];
#endif // USE_ESP32_SPI
bool use_spi;
} Ade7953;
/*********************************************************************************************/
int Ade7953RegSize(uint16_t reg) {
int size = 0;
switch ((reg >> 8) & 0x0F) {
case 0x03: // 32-bit
size++;
case 0x02: // 24-bit
size++;
case 0x01: // 16-bit
size++;
case 0x00: // 8-bit
case 0x07:
case 0x08:
size++;
}
return size;
}
#ifdef USE_ESP32_SPI
void Ade7953SpiEnable(void) {
digitalWrite(Ade7953.pin_cs[Ade7953.cs_index], 0);
delayMicroseconds(1); // CS 1uS to SCLK edge
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // Set up SPI at 1MHz, MSB first, Capture at rising edge
}
void Ade7953SpiDisable(void) {
SPI.endTransaction();
delayMicroseconds(2); // CS high 1.2uS after SCLK edge (when writing to COMM_LOCK bit)
digitalWrite(Ade7953.pin_cs[Ade7953.cs_index], 1);
}
#endif // USE_ESP32_SPI
void Ade7953Write(uint16_t reg, uint32_t val) {
int size = Ade7953RegSize(reg);
if (size) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Write %08X"), val);
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
Ade7953SpiEnable();
SPI.transfer16(reg);
SPI.transfer(0x00); // Write
while (size--) {
SPI.transfer((val >> (8 * size)) & 0xFF); // Write data, MSB first
}
Ade7953SpiDisable();
} else {
#endif // USE_ESP32_SPI
Wire.beginTransmission(ADE7953_ADDR);
Wire.write((reg >> 8) & 0xFF);
Wire.write(reg & 0xFF);
while (size--) {
Wire.write((val >> (8 * size)) & 0xFF); // Write data, MSB first
}
Wire.endTransmission();
delayMicroseconds(5); // Bus-free time minimum 4.7us
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
}
int32_t Ade7953Read(uint16_t reg) {
uint32_t response = 0;
int size = Ade7953RegSize(reg);
if (size) {
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
Ade7953SpiEnable();
SPI.transfer16(reg);
SPI.transfer(0x80); // Read
while (size--) {
response = response << 8 | SPI.transfer(0xFF); // receive DATA (MSB first)
}
Ade7953SpiDisable();
} else {
#endif // USE_ESP32_SPI
Wire.beginTransmission(ADE7953_ADDR);
Wire.write((reg >> 8) & 0xFF);
Wire.write(reg & 0xFF);
Wire.endTransmission(0);
Wire.requestFrom(ADE7953_ADDR, size);
if (size <= Wire.available()) {
for (uint32_t i = 0; i < size; i++) {
response = response << 8 | Wire.read(); // receive DATA (MSB first)
}
}
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
return response;
}
#ifdef ADE7953_DUMP_REGS
void Ade7953DumpRegs(uint32_t chip) {
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** Chip%d **** SAGCYC DISNOLD Resrvd Resrvd LCYCMOD Resrvd Resrvd PGAV PGAIA PGAIB"), chip +1);
char data[200] = { 0 };
for (uint32_t i = 0; i < 10; i++) {
int32_t value = Ade7953Read(ADE7953_SAGCYC + i);
snprintf_P(data, sizeof(data), PSTR("%s %02X"), data, value); // 8-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x000..009%s"), data);
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** ZXTOUT LINECYC CONFIG CF1DEN CF2DEN Resrvd Resrvd CFMODE PHCALA PHCALB PFA PFB ANGLEA ANGLEB Period"));
data[0] = '\0';
for (uint32_t i = 0; i < 15; i++) {
int32_t value = Ade7953Read(ADE7953_ZXTOUT + i);
snprintf_P(data, sizeof(data), PSTR("%s %04X"), data, value); // 16-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x100..10E%s"), data);
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** IGAIN VGAIN WGAIN VARGAIN VAGAIN Resrvd IRMSOS Resrvd VRMSOS WATTOS VAROS VAOS"));
data[0] = '\0';
for (uint32_t i = 0; i < 12; i++) {
int32_t value = Ade7953Read(ADE7953_AIGAIN + i);
snprintf_P(data, sizeof(data), PSTR("%s %06X"), data, value); // 24-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x380..38B%s"), data);
data[0] = '\0';
for (uint32_t i = 0; i < 12; i++) {
int32_t value = Ade7953Read(ADE7953_BIGAIN + i);
snprintf_P(data, sizeof(data), PSTR("%s %06X"), data, value); // 24-bit regs
}
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: *** 0x38C..397%s"), data);
}
#endif // ADE7953_DUMP_REGS
void Ade7953SetCalibration(uint32_t regset, uint32_t calibset) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
int32_t value = Ade7953.calib_data[calibset][i];
if (ADE7943_CAL_PHCAL == i) {
// if (ADE7953_PHCAL_DEFAULT == value) { continue; } // ADE7953 reset does NOT always reset all registers
if (value < 0) {
value = abs(value) + 0x200; // Add sign magnitude
}
}
// if (ADE7953_GAIN_DEFAULT == value) { continue; } // ADE7953 reset does NOT always reset all registers
Ade7953Write(Ade7953CalibRegs[regset][i], value);
}
}
void Ade7953Init(void) {
uint32_t chips = 1;
#ifdef USE_ESP32_SPI
chips = (Ade7953.pin_cs[1] >= 0) ? 2 : 1;
#endif // USE_ESP32_SPI
// Init ADE7953 with calibration settings
for (uint32_t chip = 0; chip < chips; chip++) {
Ade7953.cs_index = chip;
#ifdef ADE7953_DUMP_REGS
Ade7953DumpRegs(chip);
#endif // ADE7953_DUMP_REGS
Ade7953Write(ADE7953_CONFIG, 0x0004); // Locking the communication interface (Clear bit COMM_LOCK), Enable HPF
Ade7953Write(0x0FE, 0x00AD); // Unlock register 0x120
Ade7953Write(ADE7953_RESERVED_0X120, 0x0030); // Configure optimum setting
Ade7953Write(ADE7953_DISNOLOAD, 0x07); // Disable no load detection, required before setting thresholds
Ade7953Write(ADE7953_AP_NOLOAD, ADE7953_NO_LOAD_THRESHOLD); // Set no load treshold for active power
Ade7953Write(ADE7953_VAR_NOLOAD, ADE7953_NO_LOAD_THRESHOLD); // Set no load treshold for reactive power
Ade7953Write(ADE7953_DISNOLOAD, 0x00); // Enable no load detection
#ifdef USE_ESP32_SPI
// int32_t value = Ade7953Read(0x702); // Silicon version
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Chip%d version %d"), chip +1, value);
if (1 == chip) {
switch (Ade7953.model) {
case ADE7953_SHELLY_PRO_2PM:
Ade7953SetCalibration(0, 1); // Second ADE7953 A registers set with calibration set 1
break;
case ADE7953_SHELLY_PRO_4PM:
Ade7953SetCalibration(0, 2); // Second ADE7953 A registers set with calibration set 2
Ade7953SetCalibration(1, 3); // Second ADE7953 B registers set with calibration set 3
}
} else {
#endif // USE_ESP32_SPI
Ade7953SetCalibration(0, 0); // First ADE7953 A registers set with calibration set 0
switch (Ade7953.model) {
case ADE7953_SHELLY_25:
case ADE7953_SHELLY_EM:
case ADE7953_SHELLY_PLUS_2PM:
// case ADE7953_SHELLY_PRO_1PM: // Uses defaults for B registers
case ADE7953_SHELLY_PRO_4PM:
Ade7953SetCalibration(1, 1); // First ADE7953 B registers set with calibration set 1
}
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
}
// Report set calibration settings
int32_t regs[ADE7953_CALIBREGS];
for (uint32_t chip = 0; chip < chips; chip++) {
Ade7953.cs_index = chip;
for (uint32_t channel = 0; channel < 2; channel++) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
regs[i] = Ade7953Read(Ade7953CalibRegs[channel][i]);
if (ADE7943_CAL_PHCAL == i) {
if (regs[i] >= 0x0200) {
regs[i] &= 0x01FF; // Clear sign magnitude
regs[i] *= -1; // Make negative
}
}
}
#ifdef USE_ESP32_SPI
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: Chip%d CalibRegs%c V %d, I %d, W %d, VA %d, var %d, Ph %d"),
chip +1, 'A'+channel, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5]);
#else
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: CalibRegs%c V %d, I %d, W %d, VA %d, var %d, Ph %d"),
'A'+channel, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5]);
#endif // USE_ESP32_SPI
}
#ifdef ADE7953_DUMP_REGS
Ade7953DumpRegs(chip);
#endif // ADE7953_DUMP_REGS
}
}
void Ade7953GetData(void) {
uint32_t acc_mode = 0;
int32_t reg[ADE7953_MAX_CHANNEL][ADE7953_REGISTERS];
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) {
uint32_t channel = 0;
for (uint32_t chip = 0; chip < ADE7953_MAX_CHANNEL / 2; chip++) {
if (Ade7953.pin_cs[chip] < 0) { continue; }
Ade7953.cs_index = chip;
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel][i] = Ade7953Read(Ade7953Registers[0][i]); // IRMSa, WATTa, VAa, VARa, VRMS, Period
}
channel++;
if (4 == Energy->phase_count) {
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel][i] = Ade7953Read(Ade7953Registers[1][i]); // IRMSb, WATTb, VAb, VARb, VRMS, Period
}
channel++;
}
}
} else {
#endif // USE_ESP32_SPI
for (uint32_t channel = 0; channel < 2; channel++) {
uint32_t channel_swap = (ADE7953_SHELLY_25 == Ade7953.model) ? !channel : channel;
for (uint32_t i = 0; i < ADE7953_REGISTERS; i++) {
reg[channel_swap][i] = Ade7953Read(Ade7953Registers[channel][i]);
}
}
acc_mode = Ade7953Read(ADE7953_ACCMODE); // Accumulation mode
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
for (uint32_t i = 0; i < Energy->phase_count; i++) {
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: Channel%d ACCMODE 0x%06X, VRMS %d, Period %d, IRMS %d, WATT %d, VA %d, VAR %d"),
i+1, acc_mode, reg[i][4], reg[i][5], reg[i][0], reg[i][1], reg[i][2], reg[i][3]);
}
// If the device is initializing, we read the energy registers to reset them, but don't report the values as the first read may be inaccurate
if (Ade7953.init_step) { return; }
uint32_t apparent_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
uint32_t reactive_power[ADE7953_MAX_CHANNEL] = { 0, 0 };
for (uint32_t channel = 0; channel < Energy->phase_count; channel++) {
Ade7953.voltage_rms[channel] = reg[channel][4];
Ade7953.current_rms[channel] = reg[channel][0];
Ade7953.active_power[channel] = abs(reg[channel][1]);
apparent_power[channel] = abs(reg[channel][2]);
reactive_power[channel] = abs(reg[channel][3]);
if ((ADE7953_SHELLY_EM == Ade7953.model) && (bitRead(acc_mode, 18 +(channel * 3)))) { // VARNLOAD
reactive_power[channel] = 0;
}
}
if (Energy->power_on) { // Powered on
#ifdef USE_ESP32_SPI
float correction = 1.0f;
if (Ade7953.use_spi) { // SPI
uint32_t time = millis();
if (Ade7953.last_update) {
uint32_t difference = time - Ade7953.last_update;
correction = (float)difference / 1000; // Correction to 1 second
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Correction %4_f"), &correction);
}
Ade7953.last_update = time;
}
#endif // USE_ESP32_SPI
float divider;
for (uint32_t channel = 0; channel < Energy->phase_count; channel++) {
Energy->data_valid[channel] = 0;
float power_calibration = (float)EnergyGetCalibration(ENERGY_POWER_CALIBRATION, channel) / 10;
#ifdef ADE7953_ACCU_ENERGY
power_calibration /= ADE7953_POWER_CORRECTION;
#endif // ADE7953_ACCU_ENERGY
float voltage_calibration = (float)EnergyGetCalibration(ENERGY_VOLTAGE_CALIBRATION, channel);
float current_calibration = (float)EnergyGetCalibration(ENERGY_CURRENT_CALIBRATION, channel) * 10;
Energy->frequency[channel] = 223750.0f / ((float)reg[channel][5] + 1);
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VGAIN] != ADE7953_GAIN_DEFAULT) ? 10000 : voltage_calibration;
Energy->voltage[channel] = (float)Ade7953.voltage_rms[channel] / divider;
divider = (Ade7953.calib_data[channel][ADE7953_CAL_WGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->active_power[channel] = (float)Ade7953.active_power[channel] / divider;
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VARGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->reactive_power[channel] = (float)reactive_power[channel] / divider;
if (ADE7953_SHELLY_EM == Ade7953.model) {
if (bitRead(acc_mode, 10 +channel)) { // APSIGN
Energy->active_power[channel] *= -1;
}
if (bitRead(acc_mode, 12 +channel)) { // VARSIGN
Energy->reactive_power[channel] *= -1;
}
}
divider = (Ade7953.calib_data[channel][ADE7953_CAL_VAGAIN] != ADE7953_GAIN_DEFAULT) ? ADE7953_LSB_PER_WATTSECOND : power_calibration;
Energy->apparent_power[channel] = (float)apparent_power[channel] / divider;
#ifdef USE_ESP32_SPI
if (Ade7953.use_spi) { // SPI
Energy->active_power[channel] /= correction;
Energy->reactive_power[channel] /= correction;
Energy->apparent_power[channel] /= correction;
}
#endif // USE_ESP32_SPI
if (0 == Energy->active_power[channel]) {
Energy->current[channel] = 0;
} else {
divider = (Ade7953.calib_data[channel][ADE7953_CAL_IGAIN] != ADE7953_GAIN_DEFAULT) ? 100000 : current_calibration;
Energy->current[channel] = (float)Ade7953.current_rms[channel] / divider;
Energy->kWhtoday_delta[channel] += Energy->active_power[channel] * 1000 / 36;
}
}
EnergyUpdateToday();
}
}
void Ade7953EnergyEverySecond(void) {
if (Ade7953.init_step) {
if (2 == Ade7953.init_step) { Ade7953Init(); }
if (1 == Ade7953.init_step) { Ade7953GetData(); } // Read registers but do not display yet
Ade7953.init_step--;
} else {
Ade7953GetData();
}
}
/*********************************************************************************************/
bool Ade7953SetDefaults(const char* json) {
// {"angles":{"angle0":180,"angle1":176}}
// {"rms":{"current_a":4194303,"current_b":4194303,"voltage":1613194},"angles":{"angle0":0,"angle1":0},"powers":{"totactive":{"a":2723574,"b":2723574},"apparent":{"a":2723574,"b":2723574},"reactive":{"a":2723574,"b":2723574}}}
// {"rms":{"current_a":21865738,"current_b":1558533,"voltage_a":1599149,"voltage_b":1597289},"angles":{"angle0":0,"angle1":0},"powers":{"totactive":{"a":106692616,"b":3540894}}}
uint32_t len = strlen(json) +1;
if (len < 7) { return false; } // Too short
char json_buffer[len];
memcpy(json_buffer, json, len); // Keep original safe
JsonParser parser(json_buffer);
JsonParserObject root = parser.getRootObject();
if (!root) {
AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: Invalid JSON"));
return false;
}
// All parameters are optional allowing for partial changes
JsonParserToken val;
char field[20];
for (uint32_t i = 0; i < ADE7953_MAX_CHANNEL; i++) {
JsonParserObject rms = root[PSTR("rms")].getObject();
if (rms) {
val = rms[PSTR("voltage")];
if (val) {
Ade7953.calib_data[i][ADE7953_CAL_VGAIN] = val.getInt();
}
#ifdef USE_ESP32_SPI
snprintf_P(field, sizeof(field), PSTR("voltage_%c"), 'a'+i);
val = rms[field]; // "voltage_a" .. "voltage_d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VGAIN] = val.getInt(); }
#endif // USE_ESP32_SPI
snprintf_P(field, sizeof(field), PSTR("current_%c"), 'a'+i);
val = rms[field]; // "current_a" .. "current_d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_IGAIN] = val.getInt(); }
}
JsonParserObject angles = root[PSTR("angles")].getObject();
if (angles) {
snprintf_P(field, sizeof(field), PSTR("angle%c"), '0'+i);
val = angles[field]; // "angle0" .. "angle3"
if (val) { Ade7953.calib_data[i][ADE7943_CAL_PHCAL] = val.getInt(); }
}
JsonParserObject powers = root[PSTR("powers")].getObject();
if (powers) {
snprintf_P(field, sizeof(field), PSTR("%c"), 'a'+i);
JsonParserObject totactive = powers[PSTR("totactive")].getObject();
if (totactive) {
val = totactive[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_WGAIN] = val.getInt(); }
}
JsonParserObject apparent = powers[PSTR("apparent")].getObject();
if (apparent) {
val = apparent[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VAGAIN] = val.getInt(); }
}
JsonParserObject reactive = powers[PSTR("reactive")].getObject();
if (reactive) {
val = reactive[field]; // "a" .. "d"
if (val) { Ade7953.calib_data[i][ADE7953_CAL_VARGAIN] = val.getInt(); }
}
}
}
return true;
}
void Ade7953Defaults(void) {
for (uint32_t channel = 0; channel < ADE7953_MAX_CHANNEL; channel++) {
for (uint32_t i = 0; i < ADE7953_CALIBREGS; i++) {
if (ADE7943_CAL_PHCAL == i) {
Ade7953.calib_data[channel][i] = (ADE7953_SHELLY_EM == Ade7953.model) ? ADE7953_PHCAL_DEFAULT_CT : ADE7953_PHCAL_DEFAULT;
} else {
Ade7953.calib_data[channel][i] = ADE7953_GAIN_DEFAULT;
}
}
}
String calib = "";
#ifdef USE_UFILESYS
calib = TfsLoadString("/calib.dat");
#endif // USE_UFILESYS
#ifdef USE_RULES
// rule3 on file#calib.dat do {"angles":{"angle0":180,"angle1":176}} endon
if (!calib.length()) {
calib = RuleLoadFile("CALIB.DAT");
}
#endif // USE_RULES
if (calib.length()) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("ADE: File '%s'"), calib.c_str());
Ade7953SetDefaults(calib.c_str());
}
}
void Ade7953DrvInit(void) {
if (PinUsed(GPIO_ADE7953_IRQ, GPIO_ANY)) { // Irq is not supported...
uint32_t pin_irq = Pin(GPIO_ADE7953_IRQ, GPIO_ANY);
pinMode(pin_irq, INPUT); // Related to resetPins() - Must be set to input
// 0 (1 = Shelly 2.5), 1 (2 = Shelly EM), 2 (3 = Shelly Plus 2PM), 3 (4 = Shelly Pro 1PM), 4 (5 = Shelly Pro 2PM), 5 (6 = Shelly Pro 4PM)
Ade7953.model = GetPin(pin_irq) - AGPIO(GPIO_ADE7953_IRQ);
int pin_reset = Pin(GPIO_ADE7953_RST); // -1 if not defined
#ifdef ESP8266
if (ADE7953_SHELLY_EM == Ade7953.model) {
if (-1 == pin_reset) {
pin_reset = 16;
}
}
#endif
if (pin_reset >= 0) {
digitalWrite(pin_reset, 0);
pinMode(pin_reset, OUTPUT); // Reset pin ADE7953
delay(1); // To initiate a hardware reset, this pin must be brought low for a minimum of 10 μs.
digitalWrite(pin_reset, 1);
if (Ade7953.model < ADE7953_SHELLY_PRO_1PM) {
pinMode(pin_reset, INPUT);
}
}
#ifdef USE_ESP32_SPI
#if (defined(USE_SHELLY_PRO) && defined(USE_MCP23XXX_DRV)) || defined(USE_SHELLY_PRO_V2)
if (Ade7953.model == ADE7953_SHELLY_PRO_4PM) {
ShellyPro4Reset();
}
#endif // USE_SHELLY_PRO
#endif // USE_ESP32_SPI
delay(100); // Need 100mS to init ADE7953
#ifdef USE_ESP32_SPI
Ade7953.pin_cs[0] = -1;
Ade7953.pin_cs[1] = -1;
if (Ade7953.model >= ADE7953_SHELLY_PRO_1PM) { // SPI
if (PinUsed(GPIO_ADE7953_CS)) { // ADE7953 CS1 enabled (Pro 1PM/2PM)
Ade7953.pin_cs[0] = Pin(GPIO_ADE7953_CS);
digitalWrite(Ade7953.pin_cs[0], 1); // ADE7953 CS1 enabled (Pro 2PM)
pinMode(Ade7953.pin_cs[0], OUTPUT);
Ade7953.pin_cs[1] = Pin(GPIO_ADE7953_CS, 1);
if (Ade7953.pin_cs[1] > -1) { // ADE7953 CS2 enabled (Pro 2PM)
digitalWrite(Ade7953.pin_cs[1], 1);
pinMode(Ade7953.pin_cs[1], OUTPUT);
} else {
Ade7953.model = ADE7953_SHELLY_PRO_1PM;
}
Ade7953.cs_index = 0;
Ade7953.use_spi = true;
SPI.begin(Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_MOSI), -1);
AddLog(LOG_LEVEL_INFO, PSTR("SPI: ADE7953 found"));
} else {
return; // No CS pin defined
}
} else {
#endif // USE_ESP32_SPI
if (!I2cSetDevice(ADE7953_ADDR)) {
return;
}
I2cSetActiveFound(ADE7953_ADDR, "ADE7953");
#ifdef USE_ESP32_SPI
}
#endif // USE_ESP32_SPI
if (EnergyGetCalibration(ENERGY_POWER_CALIBRATION) == HLW_PREF_PULSE) {
for (uint32_t i = 0; i < ADE7953_MAX_CHANNEL; i++) {
EnergySetCalibration(ENERGY_POWER_CALIBRATION, ADE7953_PREF, i);
EnergySetCalibration(ENERGY_VOLTAGE_CALIBRATION, ADE7953_UREF, i);
EnergySetCalibration(ENERGY_CURRENT_CALIBRATION, ADE7953_IREF, i);
}
}
Ade7953Defaults();
Ade7953.init_step = 3;
// Energy->phase_count = 1;
// Energy->voltage_common = false;
// Energy->frequency_common = false;
// Energy->use_overtemp = false;
if (ADE7953_SHELLY_PRO_1PM == Ade7953.model) {
} else {
Energy->phase_count = 2; // Handle two channels as two phases
if (ADE7953_SHELLY_PRO_2PM == Ade7953.model) {
} else {
Energy->voltage_common = true; // Use common voltage
Energy->frequency_common = true; // Use common frequency
if (ADE7953_SHELLY_PRO_4PM == Ade7953.model) {
Energy->phase_count = 4;
}
}
}
Energy->use_overtemp = true; // Use global temperature for overtemp detection
if (ADE7953_SHELLY_EM == Ade7953.model) {
Energy->local_energy_active_export = true;
}
TasmotaGlobal.energy_driver = XNRG_07;
}
}
bool Ade7953Command(void) {
bool serviced = true;
if (XdrvMailbox.index > ADE7953_MAX_CHANNEL) { return false; };
uint32_t channel = XdrvMailbox.index -1;
if (ADE7953_SHELLY_PRO_4PM != Ade7953.model) {
channel = (2 == XdrvMailbox.index) ? 1 : 0;
}
uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123
if (CMND_POWERCAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_VOLTAGECAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_CURRENTCAL == Energy->command_code) {
if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; }
// Service in xdrv_03_energy.ino
}
else if (CMND_POWERSET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.active_power[channel]) {
if ((value > 100) && (value < 2000000)) { // Between 1W and 20000W
#ifdef ADE7953_ACCU_ENERGY
float power_calibration = (float)(Ade7953.active_power[channel] * 1000) / value; // 0.00 W
power_calibration *= ADE7953_POWER_CORRECTION;
XdrvMailbox.payload = (uint32_t)power_calibration; // 0.00 W
#else // No ADE7953_ACCU_ENERGY
XdrvMailbox.payload = (Ade7953.active_power[channel] * 1000) / value; // 0.00 W
#endif // ADE7953_ACCU_ENERGY
}
}
}
else if (CMND_VOLTAGESET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.voltage_rms[channel]) {
if ((value > 10000) && (value < 40000)) { // Between 100V and 400V
XdrvMailbox.payload = (Ade7953.voltage_rms[channel] * 100) / value; // 0.00 V
}
}
}
else if (CMND_CURRENTSET == Energy->command_code) {
if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) {
if ((value > 2000) && (value < 10000000)) { // Between 20mA and 100A
XdrvMailbox.payload = ((Ade7953.current_rms[channel] * 100) / value) * 100; // 0.00 mA
}
}
}
else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xnrg07(uint32_t function) {
if (!I2cEnabled(XI2C_07) && (SPI_MOSI_MISO != TasmotaGlobal.spi_enabled)) { return false; }
bool result = false;
switch (function) {
#ifdef USE_ESP32_SPI
case FUNC_ENERGY_EVERY_SECOND: // Use energy interrupt timer (fails on SPI)
if (!Ade7953.use_spi) { // No SPI
Ade7953EnergyEverySecond();
}
break;
case FUNC_EVERY_SECOND: // Use loop timer (without interrupt)
if (Ade7953.use_spi) { // SPI
Ade7953EnergyEverySecond();
}
break;
#else // ESP8266
case FUNC_ENERGY_EVERY_SECOND: // Use energy interrupt timer
Ade7953EnergyEverySecond();
break;
#endif // USE_ESP32_SPI
case FUNC_COMMAND:
result = Ade7953Command();
break;
case FUNC_PRE_INIT:
Ade7953DrvInit();
break;
}
return result;
}
#endif // USE_ADE7953
#endif // USE_ENERGY_SENSOR
#endif // USE_I2C or USE_ESP_SPI