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) 2023-2025 Mathieu Carbou
4 */
5#pragma once
6
7#include <HardwareSerial.h>
8
9#include <mutex>
10
11#ifdef MYCILA_JSON_SUPPORT
12 #include <ArduinoJson.h>
13#endif
14
15#define MYCILA_JSY_VERSION "15.3.5"
16#define MYCILA_JSY_VERSION_MAJOR 15
17#define MYCILA_JSY_VERSION_MINOR 3
18#define MYCILA_JSY_VERSION_REVISION 2
19
20// #define MYCILA_JSY_DEBUG 1
21
22// broadcast address to send requests to all devices
23#define MYCILA_JSY_ADDRESS_BROADCAST 0x00
24// default factory JSY address
25#define MYCILA_JSY_ADDRESS_DEFAULT 0x01
26// constant returned when device address is unknown
27#define MYCILA_JSY_ADDRESS_UNKNOWN 0x00
28
29// Model value for unknown JSY device
30#define MYCILA_JSY_MK_UNKNOWN 0x0000
31// Model value for JSY1031 family
32#define MYCILA_JSY_MK_1031 0x1031
33// Model value for JSY-MK-163 family
34#define MYCILA_JSY_MK_163 0x0163
35// Model value for JSY-MK-193 family
36#define MYCILA_JSY_MK_193 0x0193
37// Model value for JSY-MK-194 family
38#define MYCILA_JSY_MK_194 0x0194
39// Model value for JSY-MK-227 family
40#define MYCILA_JSY_MK_227 0x0227
41// Model value for JSY-MK-229 family
42#define MYCILA_JSY_MK_229 0x0229
43// Model value for JSY-MK-333 family
44#define MYCILA_JSY_MK_333 0x0333
45
46#define MYCILA_JSY_MK_1031_NAME "JSY1031"
47#define MYCILA_JSY_MK_163_NAME "JSY-MK-163"
48#define MYCILA_JSY_MK_193_NAME "JSY-MK-193"
49#define MYCILA_JSY_MK_194_NAME "JSY-MK-194"
50#define MYCILA_JSY_MK_227_NAME "JSY-MK-227"
51#define MYCILA_JSY_MK_229_NAME "JSY-MK-229"
52#define MYCILA_JSY_MK_333_NAME "JSY-MK-333"
53
54#ifndef MYCILA_JSY_ASYNC_CORE
55 #define MYCILA_JSY_ASYNC_CORE 1
56#endif
57
58#ifndef MYCILA_JSY_ASYNC_PRIORITY
59 #define MYCILA_JSY_ASYNC_PRIORITY 5
60#endif
61
62#ifndef MYCILA_JSY_ASYNC_STACK_SIZE
63 #define MYCILA_JSY_ASYNC_STACK_SIZE 4094
64#endif
65
66// time in milliseconds to wait between each read in async mode
67#ifndef MYCILA_JSY_ASYNC_READ_PAUSE_MS
68 #define MYCILA_JSY_ASYNC_READ_PAUSE_MS 0
69#endif
70
71// Maximumtime in milliseconds to wait for bytes to arrive when reading a JSY response
72// This value is conservative and work with all JSY.
73// If you happen to only use specific JSY models, you can reduce this value to speed up the read process.
74// Only JSY1031 are slow and require such a big timeout.
75// For other models, the timeout can be decreased safely to 500 ms.
76#ifndef MYCILA_JSY_READ_TIMEOUT_MS
77 #define MYCILA_JSY_READ_TIMEOUT_MS 1000
78#endif
79
80#ifndef MYCILA_JSY_RETRY_COUNT
81 #define MYCILA_JSY_RETRY_COUNT 3
82#endif
83
84namespace Mycila {
85 class JSY {
86 public:
87 enum BaudRate : uint32_t {
88 UNKNOWN = 0,
89 // Supported by: JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229
90 BAUD_1200 = 1200,
91 // Supported by: JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229
92 BAUD_2400 = 2400,
93 // Default for: JSY-MK-163, JSY-MK-194
94 // Supported by: JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333
95 BAUD_4800 = 4800,
96 // Default for: JSY1031, JSY-MK-193, JSY-MK-227, JSY-MK-229, JSY-MK-333
97 // Supported by: JSY1031, JSY-MK-163, JSY-MK-193, JSY-MK-194, JSY-MK-227, JSY-MK-229, JSY-MK-333
98 BAUD_9600 = 9600,
99 // Supported by: JSY1031, JSY-MK-193, JSY-MK-194, JSY-MK-333
100 BAUD_19200 = 19200,
101 // Supported by: JSY-MK-193, JSY-MK-194
102 BAUD_38400 = 38400,
103 };
104
105 enum class EventType {
106 // JSY has successfully read the data
107 EVT_READ,
108 // wrong data received when reading values
109 EVT_READ_ERROR,
110 // timeout reached when reading values
111 EVT_READ_TIMEOUT,
112 // wrong JSY device read
113 EVT_READ_PEER
114 };
115
116 enum class Mode {
117 UNKNOWN,
118 AC,
119 DC
120 };
121
122 class Metrics {
123 public:
128 float frequency = NAN; // Hz
129
134 float voltage = NAN;
135
140 float current = NAN;
141
146 float activePower = NAN;
147
152 float powerFactor = NAN;
153
159 float apparentPower = NAN;
160
166 float reactivePower = NAN;
167
173 uint32_t activeEnergy = 0;
174
181
188
193 uint32_t reactiveEnergy = 0;
194
201
208
213 uint32_t apparentEnergy = 0;
214
219 float phaseAngleU = NAN;
220
225 float phaseAngleI = NAN;
226
231 float phaseAngleUI = NAN;
232
237 float thdU = NAN;
238
243 float thdI = NAN;
244
252 float thdi(float phi = 0) const;
253
257 float resistance() const;
258
263 float dimmedVoltage() const;
264
269 float nominalPower() const;
270
271 // clear all values
272 void clear();
273
274 // compare two metrics
275 bool operator==(const Metrics& other) const;
276 // compare two metrics
277 bool operator!=(const Metrics& other) const { return !(*this == other); }
278 // add two metrics, averaging the voltage
279 Metrics& operator+=(const Metrics& other);
280 // copy a metric
281 void operator=(const Metrics& other);
282
283#ifdef MYCILA_JSON_SUPPORT
284 void toJson(const JsonObject& root) const;
285#endif
286 };
287
288 class Data {
289 public:
290 uint8_t address = MYCILA_JSY_ADDRESS_UNKNOWN; // device address
291 uint16_t model = MYCILA_JSY_MK_UNKNOWN; // device model
292
293 // For JSY1031: aggregate == single()
294 // For JSY-MK-163: aggregate == single()
295 // For JSY-MK-194: aggregate == channel1() + channel2()
296 // For JSY-MK-333: aggregate == phaseA() + phaseB() + phaseC()
297 Metrics aggregate;
298
299 // JSY-MK-163 and JSY1031
300 const Metrics& single() const { return _metrics[0]; }
301
302 // JSY-MK-194 (channel 1)
303 const Metrics& channel1() const { return _metrics[0]; }
304 // JSY-MK-194 (channel 2)
305 const Metrics& channel2() const { return _metrics[1]; }
306
307 // JSY-MK-333 (phase A)
308 const Metrics& phaseA() const { return _metrics[0]; }
309 // JSY-MK-333 (phase B)
310 const Metrics& phaseB() const { return _metrics[1]; }
311 // JSY-MK-333 (phase C)
312 const Metrics& phaseC() const { return _metrics[2]; }
313
314 // clear all values
315 void clear();
316
317 // compare two data
318 bool operator==(const Data& other) const;
319 // compare two data
320 bool operator!=(const Data& other) const { return !(*this == other); }
321 // copy a data
322 void operator=(const Data& other);
323
324#ifdef MYCILA_JSON_SUPPORT
325 void toJson(const JsonObject& root) const;
326#endif
327
328 private:
329 friend class JSY;
330 Metrics _metrics[3];
331 };
332
333 typedef std::function<void(EventType eventType, const Data& data)> Callback;
334
335 ~JSY() { end(); }
336
348 void begin(HardwareSerial& serial, // NOLINT
349 int8_t rxPin,
350 int8_t txPin,
351 bool async,
352 uint8_t core = MYCILA_JSY_ASYNC_CORE,
353 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
354 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS) {
355 begin(serial, rxPin, txPin, BaudRate::UNKNOWN, MYCILA_JSY_ADDRESS_BROADCAST, MYCILA_JSY_MK_UNKNOWN, async, core, stackSize, pause);
356 }
357
371 void begin(HardwareSerial& serial, // NOLINT
372 int8_t rxPin,
373 int8_t txPin,
374 BaudRate baudRate = BaudRate::UNKNOWN,
375 uint8_t destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST,
376 uint16_t model = MYCILA_JSY_MK_UNKNOWN,
377 bool async = false,
378 uint8_t core = MYCILA_JSY_ASYNC_CORE,
379 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
380 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS);
381
385 void end();
386
394 bool setDeviceAddress(uint8_t newAddress) { return setDeviceAddress(_destinationAddress, newAddress); }
395
404 bool setDeviceAddress(uint8_t address, uint8_t newAddress);
405
411 uint16_t readModel() { return readModel(_destinationAddress); }
412
419 uint16_t readModel(uint8_t address);
420
425 uint16_t getModel() const { return _model; }
426
427 const char* getModelName() const { return getModelName(_model); }
428
434 static const char* getModelName(uint16_t model);
435
441 Mode readMode() { return _readMode(_destinationAddress, _model); }
442
449 Mode readMode(uint8_t address) { return _readMode(address, readModel(address)); }
450
457 bool setMode(Mode mode) { return _setMode(_destinationAddress, _model, mode); }
458
466 bool setMode(uint8_t address, Mode mode) { return _setMode(address, readModel(address), mode); }
467
473 bool read() { return _read(_destinationAddress, _model); }
474
481 bool read(uint8_t address) { return _read(address, readModel(address)); }
482
488 bool resetEnergy() { return resetEnergy(_destinationAddress); }
489
496 bool resetEnergy(uint8_t address);
497
498 // Try to change the baud rate of the JSY. Returns true if the baud rate was changed.
499 // This function is blocking until the change is confirmed or the timeout is reached.
500
507 bool setBaudRate(BaudRate baudRate) { return setBaudRate(_destinationAddress, baudRate); }
508
516 bool setBaudRate(uint8_t address, BaudRate baudRate);
517
518#ifdef MYCILA_JSON_SUPPORT
519 void toJson(const JsonObject& root) const;
520#endif
521
522 gpio_num_t getRXPin() const { return _pinRX; }
523 gpio_num_t getTXPin() const { return _pinTX; }
524 bool isEnabled() const { return _enabled; }
525 BaudRate getBaudRate() const { return _baudRate; }
526
531 uint8_t getDestinationAddress() const { return _destinationAddress; }
532
533 BaudRate getMinAvailableBaudRate() const;
534 static BaudRate getMinAvailableBaudRate(uint16_t model);
535
540 BaudRate getMaxAvailableBaudRate() const;
541 static BaudRate getMaxAvailableBaudRate(uint16_t model);
542
547 bool isBaudRateSupported(BaudRate baudRate) const;
548 static bool isBaudRateSupported(uint16_t model, BaudRate baudRate);
549
554 uint8_t getLastAddress() const { return _lastAddress; }
555
559 uint32_t getTime() const { return _time; }
560
561 // check if the device is connected to the grid, meaning if last read was successful
562 bool isConnected() const { return _data.aggregate.frequency > 0; }
563
564 void setCallback(Callback callback) { _callback = callback; }
565
566 private:
567 Callback _callback = nullptr;
568 gpio_num_t _pinRX = GPIO_NUM_NC;
569 gpio_num_t _pinTX = GPIO_NUM_NC;
570 HardwareSerial* _serial = nullptr;
571 std::mutex _mutex;
572 TaskHandle_t _taskHandle;
573 uint32_t _time = 0;
574 uint32_t _pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS;
575 uint8_t _destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST;
576 uint8_t _lastAddress = MYCILA_JSY_ADDRESS_UNKNOWN;
577 BaudRate _baudRate = BaudRate::UNKNOWN;
578 bool _enabled = false;
579 uint16_t _model = MYCILA_JSY_MK_UNKNOWN;
580 // buffer to read/write data
581 // biggest need is for JSY-MK-333: 102 registers of 2 bytes each + 5 bytes for the response: 209 bytes
582 // we use 14 blocks of 16 bytes: 224 bytes
583 uint8_t _buffer[224];
584 Data _data;
585
586 private:
587 enum class ReadResult {
588 READ_SUCCESS = 0,
589 READ_TIMEOUT,
590 READ_ERROR_COUNT,
591 READ_ERROR_CRC,
592 READ_ERROR_ADDRESS,
593 };
594
595 bool _set(uint8_t address, uint8_t newAddress, BaudRate newBaudRate);
596 bool _read(uint8_t address, uint16_t model);
597 Mode _readMode(uint8_t address, uint16_t model);
598 bool _setMode(uint8_t address, uint16_t model, Mode mode);
599
600 bool _canRead(uint8_t address, BaudRate baudRate);
601 ReadResult _timedRead(uint8_t expectedAddress, size_t expectedLen, BaudRate baudRate);
602 void _send(uint8_t address, size_t len);
603 size_t _drop();
604 void _openSerial(BaudRate baudRate);
605 BaudRate _detectBauds(uint8_t address);
606
607 static uint16_t _crc16(const uint8_t* buffer, size_t len);
608 static uint8_t _register8(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress, uint8_t index = 0);
609 static uint16_t _register16(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
610 static uint32_t _register32(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
611 static void _jsyTask(void* pvParameters);
612 };
613} // 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:166
float phaseAngleU
voltage phase angle in degrees (°).
Definition MycilaJSY.h:219
float apparentPower
Apparent power in volt-amperes (VA). Always positive.
Definition MycilaJSY.h:159
float thdU
total harmonic distortion of voltage (THDu), as a percentage (%).
Definition MycilaJSY.h:237
float powerFactor
Power factor. Positive value between 0 and 1.
Definition MycilaJSY.h:152
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:193
float activePower
Active power in watts (W). Can be positive or negative.
Definition MycilaJSY.h:146
uint32_t activeEnergyImported
Active energy imported in watt-hours (Wh), going to the load, when activePower > 0.
Definition MycilaJSY.h:180
float current
Current in amperes (A).
Definition MycilaJSY.h:140
uint32_t activeEnergy
Active energy in watt-hours (Wh).
Definition MycilaJSY.h:173
float phaseAngleI
current phase angle in degrees (°).
Definition MycilaJSY.h:225
float thdI
total harmonic distortion of current (THDi) as a percentage (%).
Definition MycilaJSY.h:243
float phaseAngleUI
voltage - current phase angle displacement in degrees (°).
Definition MycilaJSY.h:231
uint32_t activeEnergyReturned
Active energy returned in watt-hours (Wh), coming from the load, when activePower < 0.
Definition MycilaJSY.h:187
float voltage
Voltage in volts (V).
Definition MycilaJSY.h:134
uint32_t reactiveEnergyReturned
Reactive energy returned in volt-amperes reactive-hours (VArh), coming from the load,...
Definition MycilaJSY.h:207
uint32_t reactiveEnergyImported
Reactive energy imported in volt-amperes reactive-hours (VArh), going to the load,...
Definition MycilaJSY.h:200
float dimmedVoltage() const
Compute the dimmed voltage (V = P / I).
float frequency
Frequency in hertz (Hz).
Definition MycilaJSY.h:128
uint32_t apparentEnergy
Apparent energy in volt-amperes-hours (VAh).
Definition MycilaJSY.h:213
uint16_t getModel() const
Get the cached JSY model that was read during begin()
Definition MycilaJSY.h:425
bool read()
Read the JSY values.
Definition MycilaJSY.h:473
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:348
uint8_t getLastAddress() const
Get the address of the last device's response.
Definition MycilaJSY.h:554
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:457
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:449
bool setDeviceAddress(uint8_t newAddress)
Set a new address for a device.
Definition MycilaJSY.h:394
bool resetEnergy()
Reset the energy counters of the JSY.
Definition MycilaJSY.h:488
uint32_t getTime() const
Definition MycilaJSY.h:559
bool setBaudRate(BaudRate baudRate)
Change the baud rate of the JSY.
Definition MycilaJSY.h:507
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:481
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:466
Mode readMode()
Reads the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:441
uint16_t readModel()
Reads the JSY model.
Definition MycilaJSY.h:411
uint8_t getDestinationAddress() const
Get the address used to send requests.
Definition MycilaJSY.h:531