MycilaJSY 13.0.0
Arduino / ESP32 library for the JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333 families single-phase and three-phase AC bidirectional meters from Shenzhen Jiansiyan Technologies Co, Ltd.
Loading...
Searching...
No Matches
MycilaJSY.h
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright (C) Mathieu Carbou
4 */
5#pragma once
6
7#include <HardwareSerial.h>
8
9#include <mutex>
10#include <utility>
11
12#ifdef MYCILA_JSON_SUPPORT
13 #include <ArduinoJson.h>
14#endif
15
16#define MYCILA_JSY_VERSION "15.3.11"
17#define MYCILA_JSY_VERSION_MAJOR 15
18#define MYCILA_JSY_VERSION_MINOR 3
19#define MYCILA_JSY_VERSION_REVISION 10
20
21// #define MYCILA_JSY_DEBUG 1
22
23// broadcast address to send requests to all devices
24#define MYCILA_JSY_ADDRESS_BROADCAST 0x00
25// default factory JSY address
26#define MYCILA_JSY_ADDRESS_DEFAULT 0x01
27// constant returned when device address is unknown
28#define MYCILA_JSY_ADDRESS_UNKNOWN 0x00
29
30// Model value for unknown JSY device
31#define MYCILA_JSY_MK_UNKNOWN 0x0000
32// Model value for JSY1031 family
33#define MYCILA_JSY_MK_1031 0x1031
34// Model value for JSY-MK-163 family
35#define MYCILA_JSY_MK_163 0x0163
36// Model value for JSY-MK-193 family
37#define MYCILA_JSY_MK_193 0x0193
38// Model value for JSY-MK-194 family
39#define MYCILA_JSY_MK_194 0x0194
40// Model value for JSY-MK-227 family
41#define MYCILA_JSY_MK_227 0x0227
42// Model value for JSY-MK-229 family
43#define MYCILA_JSY_MK_229 0x0229
44// Model value for JSY-MK-333 family
45#define MYCILA_JSY_MK_333 0x0333
46
47#define MYCILA_JSY_MK_1031_NAME "JSY1031"
48#define MYCILA_JSY_MK_163_NAME "JSY-MK-163"
49#define MYCILA_JSY_MK_193_NAME "JSY-MK-193"
50#define MYCILA_JSY_MK_194_NAME "JSY-MK-194"
51#define MYCILA_JSY_MK_227_NAME "JSY-MK-227"
52#define MYCILA_JSY_MK_229_NAME "JSY-MK-229"
53#define MYCILA_JSY_MK_333_NAME "JSY-MK-333"
54
55#ifndef MYCILA_JSY_ASYNC_CORE
56 #define MYCILA_JSY_ASYNC_CORE 1
57#endif
58
59#ifndef MYCILA_JSY_ASYNC_PRIORITY
60 #define MYCILA_JSY_ASYNC_PRIORITY 5
61#endif
62
63#ifndef MYCILA_JSY_ASYNC_STACK_SIZE
64 #define MYCILA_JSY_ASYNC_STACK_SIZE 4094
65#endif
66
67// time in milliseconds to wait between each read in async mode
68#ifndef MYCILA_JSY_ASYNC_READ_PAUSE_MS
69 #define MYCILA_JSY_ASYNC_READ_PAUSE_MS 0
70#endif
71
72// Maximumtime in milliseconds to wait for bytes to arrive when reading a JSY response
73// This value is conservative and work with all JSY.
74// If you happen to only use specific JSY models, you can reduce this value to speed up the read process.
75// Only JSY1031 are slow and require such a big timeout.
76// For other models, the timeout can be decreased safely to 500 ms.
77#ifndef MYCILA_JSY_READ_TIMEOUT_MS
78 #define MYCILA_JSY_READ_TIMEOUT_MS 1000
79#endif
80
81#ifndef MYCILA_JSY_RETRY_COUNT
82 #define MYCILA_JSY_RETRY_COUNT 3
83#endif
84
85namespace Mycila {
86 class JSY {
87 public:
88 enum BaudRate : uint32_t {
89 UNKNOWN = 0,
90 // Supported by: JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229
91 BAUD_1200 = 1200,
92 // Supported by: JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229
93 BAUD_2400 = 2400,
94 // Default for: JSY-MK-163, JSY-MK-194
95 // Supported by: JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333
96 BAUD_4800 = 4800,
97 // Default for: JSY1031, JSY-MK-193, JSY-MK-227, JSY-MK-229, JSY-MK-333
98 // Supported by: JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333
99 BAUD_9600 = 9600,
100 // Supported by: JSY1031, JSY-MK-193, JSY-MK-194, JSY-MK-333
101 BAUD_19200 = 19200,
102 // Supported by: JSY-MK-193, JSY-MK-194
103 BAUD_38400 = 38400,
104 };
105
106 enum class EventType {
107 // JSY has successfully read the data
108 EVT_READ,
109 // wrong data received when reading values
110 EVT_READ_ERROR,
111 // timeout reached when reading values
112 EVT_READ_TIMEOUT,
113 // wrong JSY device read
114 EVT_READ_PEER
115 };
116
117 enum class Mode {
118 UNKNOWN,
119 AC,
120 DC
121 };
122
123 class Metrics {
124 public:
129 float frequency = NAN; // Hz
130
135 float voltage = NAN;
136
141 float current = NAN;
142
147 float activePower = NAN;
148
153 float powerFactor = NAN;
154
160 float apparentPower = NAN;
161
167 float reactivePower = NAN;
168
174 uint32_t activeEnergy = 0;
175
182
189
194 uint32_t reactiveEnergy = 0;
195
202
209
214 uint32_t apparentEnergy = 0;
215
220 float phaseAngleU = NAN;
221
226 float phaseAngleI = NAN;
227
232 float phaseAngleUI = NAN;
233
238 float thdU = NAN;
239
244 float thdI = NAN;
245
253 float thdi(float phi = 0) const;
254
258 float resistance() const;
259
264 float dimmedVoltage() const;
265
270 float nominalPower() const;
271
272 // clear all values
273 void clear();
274
275 // compare two metrics
276 bool operator==(const Metrics& other) const;
277 // compare two metrics
278 bool operator!=(const Metrics& other) const { return !(*this == other); }
279 // add two metrics, averaging the voltage
280 Metrics& operator+=(const Metrics& other);
281 // copy a metric
282 void operator=(const Metrics& other);
283
284#ifdef MYCILA_JSON_SUPPORT
285 void toJson(const JsonObject& root) const;
286#endif
287 };
288
289 class Data {
290 public:
291 uint8_t address = MYCILA_JSY_ADDRESS_UNKNOWN; // device address
292 uint16_t model = MYCILA_JSY_MK_UNKNOWN; // device model
293
294 // For JSY1031: aggregate == single()
295 // For JSY-MK-163: aggregate == single()
296 // For JSY-MK-194: aggregate == channel1() + channel2()
297 // For JSY-MK-333: aggregate == phaseA() + phaseB() + phaseC()
298 Metrics aggregate;
299
300 // JSY-MK-163 and JSY1031
301 const Metrics& single() const { return _metrics[0]; }
302
303 // JSY-MK-194 (channel 1)
304 const Metrics& channel1() const { return _metrics[0]; }
305 // JSY-MK-194 (channel 2)
306 const Metrics& channel2() const { return _metrics[1]; }
307 // generic channel access: 0 = channel1, 1 = channel2
308 const Metrics& channel(size_t index) const { return _metrics[index]; }
309
310 // JSY-MK-333 (phase A)
311 const Metrics& phaseA() const { return _metrics[0]; }
312 // JSY-MK-333 (phase B)
313 const Metrics& phaseB() const { return _metrics[1]; }
314 // JSY-MK-333 (phase C)
315 const Metrics& phaseC() const { return _metrics[2]; }
316 // generic phase access: 0 = phaseA, 1 = phaseB, 2 = phaseC
317 const Metrics& phase(size_t index) const { return _metrics[index]; }
318
319 // clear all values
320 void clear();
321
322 // compare two data
323 bool operator==(const Data& other) const;
324 // compare two data
325 bool operator!=(const Data& other) const { return !(*this == other); }
326 // copy a data
327 void operator=(const Data& other);
328
329#ifdef MYCILA_JSON_SUPPORT
330 void toJson(const JsonObject& root) const;
331#endif
332
333 private:
334 friend class JSY;
335 Metrics _metrics[3];
336 };
337
338 typedef std::function<void(EventType eventType, const Data& data)> Callback;
339
340 ~JSY() { end(); }
341
353 void begin(HardwareSerial& serial, // NOLINT
354 int8_t rxPin,
355 int8_t txPin,
356 bool async,
357 uint8_t core = MYCILA_JSY_ASYNC_CORE,
358 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
359 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS) {
360 begin(serial, rxPin, txPin, BaudRate::UNKNOWN, MYCILA_JSY_ADDRESS_BROADCAST, MYCILA_JSY_MK_UNKNOWN, async, core, stackSize, pause);
361 }
362
376 void begin(HardwareSerial& serial, // NOLINT
377 int8_t rxPin,
378 int8_t txPin,
379 BaudRate baudRate = BaudRate::UNKNOWN,
380 uint8_t destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST,
381 uint16_t model = MYCILA_JSY_MK_UNKNOWN,
382 bool async = false,
383 uint8_t core = MYCILA_JSY_ASYNC_CORE,
384 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
385 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS);
386
390 void end();
391
399 bool setDeviceAddress(uint8_t newAddress) { return setDeviceAddress(_destinationAddress, newAddress); }
400
409 bool setDeviceAddress(uint8_t address, uint8_t newAddress);
410
416 uint16_t readModel() { return readModel(_destinationAddress); }
417
424 uint16_t readModel(uint8_t address);
425
430 uint16_t getModel() const { return _model; }
431
432 const char* getModelName() const { return getModelName(_model); }
433
439 static const char* getModelName(uint16_t model);
440
446 Mode readMode() { return _readMode(_destinationAddress, _model); }
447
454 Mode readMode(uint8_t address) { return _readMode(address, readModel(address)); }
455
462 bool setMode(Mode mode) { return _setMode(_destinationAddress, _model, mode); }
463
471 bool setMode(uint8_t address, Mode mode) { return _setMode(address, readModel(address), mode); }
472
478 bool read() { return _read(_destinationAddress, _model); }
479
486 bool read(uint8_t address) { return _read(address, readModel(address)); }
487
493 bool resetEnergy() { return resetEnergy(_destinationAddress); }
494
501 bool resetEnergy(uint8_t address);
502
503 // Try to change the baud rate of the JSY. Returns true if the baud rate was changed.
504 // This function is blocking until the change is confirmed or the timeout is reached.
505
512 bool setBaudRate(BaudRate baudRate) { return setBaudRate(_destinationAddress, baudRate); }
513
521 bool setBaudRate(uint8_t address, BaudRate baudRate);
522
523#ifdef MYCILA_JSON_SUPPORT
524 void toJson(const JsonObject& root) const;
525#endif
526
527 gpio_num_t getRXPin() const { return _pinRX; }
528 gpio_num_t getTXPin() const { return _pinTX; }
529 bool isEnabled() const { return _enabled; }
530 BaudRate getBaudRate() const { return _baudRate; }
531
536 uint8_t getDestinationAddress() const { return _destinationAddress; }
537
538 BaudRate getMinAvailableBaudRate() const;
539 static BaudRate getMinAvailableBaudRate(uint16_t model);
540
545 BaudRate getMaxAvailableBaudRate() const;
546 static BaudRate getMaxAvailableBaudRate(uint16_t model);
547
552 bool isBaudRateSupported(BaudRate baudRate) const;
553 static bool isBaudRateSupported(uint16_t model, BaudRate baudRate);
554
559 uint8_t getLastAddress() const { return _lastAddress; }
560
564 uint32_t getTime() const { return _time; }
565
566 // check if the device is connected to the grid, meaning if last read was successful
567 bool isConnected() const { return _data.aggregate.frequency > 0; }
568
569 void setCallback(Callback callback) { _callback = std::move(callback); }
570
571 private:
572 Callback _callback = nullptr;
573 gpio_num_t _pinRX = GPIO_NUM_NC;
574 gpio_num_t _pinTX = GPIO_NUM_NC;
575 HardwareSerial* _serial = nullptr;
576 std::mutex _mutex;
577 TaskHandle_t _taskHandle;
578 uint32_t _time = 0;
579 uint32_t _pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS;
580 uint8_t _destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST;
581 uint8_t _lastAddress = MYCILA_JSY_ADDRESS_UNKNOWN;
582 BaudRate _baudRate = BaudRate::UNKNOWN;
583 bool _enabled = false;
584 uint16_t _model = MYCILA_JSY_MK_UNKNOWN;
585 // buffer to read/write data
586 // biggest need is for JSY-MK-333: 102 registers of 2 bytes each + 5 bytes for the response: 209 bytes
587 // we use 14 blocks of 16 bytes: 224 bytes
588 uint8_t _buffer[224];
589 Data _data;
590
591 private:
592 enum class ReadResult {
593 READ_SUCCESS = 0,
594 READ_TIMEOUT,
595 READ_ERROR_COUNT,
596 READ_ERROR_CRC,
597 READ_ERROR_ADDRESS,
598 };
599
600 bool _set(uint8_t address, uint8_t newAddress, BaudRate newBaudRate);
601 bool _read(uint8_t address, uint16_t model);
602 Mode _readMode(uint8_t address, uint16_t model);
603 bool _setMode(uint8_t address, uint16_t model, Mode mode);
604
605 bool _canRead(uint8_t address, BaudRate baudRate);
606 ReadResult _timedRead(uint8_t expectedAddress, size_t expectedLen, BaudRate baudRate);
607 void _send(uint8_t address, size_t len);
608 size_t _drop();
609 void _openSerial(BaudRate baudRate);
610 BaudRate _detectBauds(uint8_t address);
611
612 static uint16_t _crc16(const uint8_t* buffer, size_t len);
613 static uint8_t _register8(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress, uint8_t index = 0);
614 static uint16_t _register16(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
615 static uint32_t _register32(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
616 static void _jsyTask(void* pvParameters);
617 };
618} // namespace Mycila
float resistance() const
Compute the resistance of the load in ohms (R = P / I^2).
float reactivePower
Reactive power in volt-amperes reactive (VAr). Can be positive or negative.
Definition MycilaJSY.h:167
float phaseAngleU
voltage phase angle in degrees (°).
Definition MycilaJSY.h:220
float apparentPower
Apparent power in volt-amperes (VA). Always positive.
Definition MycilaJSY.h:160
float thdU
total harmonic distortion of voltage (THDu), as a percentage (%).
Definition MycilaJSY.h:238
float powerFactor
Power factor. Positive value between 0 and 1.
Definition MycilaJSY.h:153
float thdi(float phi=0) const
Compute the total harmonic distortion percentage of current (THDi). This assumes THDu = 0 (perfect vo...
float nominalPower() const
Compute the nominal power of the load in watts (P = V^2 / R).
uint32_t reactiveEnergy
Reactive energy in volt-amperes reactive-hours (VArh).
Definition MycilaJSY.h:194
float activePower
Active power in watts (W). Can be positive or negative.
Definition MycilaJSY.h:147
uint32_t activeEnergyImported
Active energy imported in watt-hours (Wh), going to the load, when activePower > 0.
Definition MycilaJSY.h:181
float current
Current in amperes (A).
Definition MycilaJSY.h:141
uint32_t activeEnergy
Active energy in watt-hours (Wh).
Definition MycilaJSY.h:174
float phaseAngleI
current phase angle in degrees (°).
Definition MycilaJSY.h:226
float thdI
total harmonic distortion of current (THDi) as a percentage (%).
Definition MycilaJSY.h:244
float phaseAngleUI
voltage - current phase angle displacement in degrees (°).
Definition MycilaJSY.h:232
uint32_t activeEnergyReturned
Active energy returned in watt-hours (Wh), coming from the load, when activePower < 0.
Definition MycilaJSY.h:188
float voltage
Voltage in volts (V).
Definition MycilaJSY.h:135
uint32_t reactiveEnergyReturned
Reactive energy returned in volt-amperes reactive-hours (VArh), coming from the load,...
Definition MycilaJSY.h:208
uint32_t reactiveEnergyImported
Reactive energy imported in volt-amperes reactive-hours (VArh), going to the load,...
Definition MycilaJSY.h:201
float dimmedVoltage() const
Compute the dimmed voltage (V = P / I).
float frequency
Frequency in hertz (Hz).
Definition MycilaJSY.h:129
uint32_t apparentEnergy
Apparent energy in volt-amperes-hours (VAh).
Definition MycilaJSY.h:214
uint16_t getModel() const
Get the cached JSY model that was read during begin().
Definition MycilaJSY.h:430
bool read()
Read the JSY values.
Definition MycilaJSY.h:478
void begin(HardwareSerial &serial, int8_t rxPin, int8_t txPin, bool async, uint8_t core=MYCILA_JSY_ASYNC_CORE, uint32_t stackSize=MYCILA_JSY_ASYNC_STACK_SIZE, uint32_t pause=MYCILA_JSY_ASYNC_READ_PAUSE_MS)
Initialize the JSY with the given RX and TX pins.
Definition MycilaJSY.h:353
uint8_t getLastAddress() const
Get the address of the last device's response.
Definition MycilaJSY.h:559
bool setMode(Mode mode)
Set the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:462
Mode readMode(uint8_t address)
Reads the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:454
bool setDeviceAddress(uint8_t newAddress)
Set a new address for a device.
Definition MycilaJSY.h:399
bool resetEnergy()
Reset the energy counters of the JSY.
Definition MycilaJSY.h:493
uint32_t getTime() const
Definition MycilaJSY.h:564
bool setBaudRate(BaudRate baudRate)
Change the baud rate of the JSY.
Definition MycilaJSY.h:512
BaudRate getMaxAvailableBaudRate() const
Get the maximum available baud rate supported by the current JSY model connected.
bool isBaudRateSupported(BaudRate baudRate) const
Check if a baud rate is supported by the current JSY model connected.
bool read(uint8_t address)
Read the JSY values.
Definition MycilaJSY.h:486
void end()
Ends the JSY communication.
bool setMode(uint8_t address, Mode mode)
Set the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:471
Mode readMode()
Reads the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:446
uint16_t readModel()
Reads the JSY model.
Definition MycilaJSY.h:416
uint8_t getDestinationAddress() const
Get the address used to send requests.
Definition MycilaJSY.h:536