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.10"
16#define MYCILA_JSY_VERSION_MAJOR 15
17#define MYCILA_JSY_VERSION_MINOR 3
18#define MYCILA_JSY_VERSION_REVISION 8
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
84 namespace 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
180 uint32_t activeEnergyImported = 0;
181
187 uint32_t activeEnergyReturned = 0;
188
193 uint32_t reactiveEnergy = 0;
194
200 uint32_t reactiveEnergyImported = 0;
201
207 uint32_t reactiveEnergyReturned = 0;
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 // generic channel access: 0 = channel1, 1 = channel2
307 const Metrics& channel(size_t index) const { return _metrics[index]; }
308
309 // JSY-MK-333 (phase A)
310 const Metrics& phaseA() const { return _metrics[0]; }
311 // JSY-MK-333 (phase B)
312 const Metrics& phaseB() const { return _metrics[1]; }
313 // JSY-MK-333 (phase C)
314 const Metrics& phaseC() const { return _metrics[2]; }
315 // generic phase access: 0 = phaseA, 1 = phaseB, 2 = phaseC
316 const Metrics& phase(size_t index) const { return _metrics[index]; }
317
318 // clear all values
319 void clear();
320
321 // compare two data
322 bool operator==(const Data& other) const;
323 // compare two data
324 bool operator!=(const Data& other) const { return !(*this == other); }
325 // copy a data
326 void operator=(const Data& other);
327
328#ifdef MYCILA_JSON_SUPPORT
329 void toJson(const JsonObject& root) const;
330#endif
331
332 private:
333 friend class JSY;
334 Metrics _metrics[3];
335 };
336
337 typedef std::function<void(EventType eventType, const Data& data)> Callback;
338
339 ~JSY() { end(); }
340
352 void begin(HardwareSerial& serial, // NOLINT
353 int8_t rxPin,
354 int8_t txPin,
355 bool async,
356 uint8_t core = MYCILA_JSY_ASYNC_CORE,
357 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
358 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS) {
359 begin(serial, rxPin, txPin, BaudRate::UNKNOWN, MYCILA_JSY_ADDRESS_BROADCAST, MYCILA_JSY_MK_UNKNOWN, async, core, stackSize, pause);
360 }
361
375 void begin(HardwareSerial& serial, // NOLINT
376 int8_t rxPin,
377 int8_t txPin,
378 BaudRate baudRate = BaudRate::UNKNOWN,
379 uint8_t destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST,
380 uint16_t model = MYCILA_JSY_MK_UNKNOWN,
381 bool async = false,
382 uint8_t core = MYCILA_JSY_ASYNC_CORE,
383 uint32_t stackSize = MYCILA_JSY_ASYNC_STACK_SIZE,
384 uint32_t pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS);
385
389 void end();
390
398 bool setDeviceAddress(uint8_t newAddress) { return setDeviceAddress(_destinationAddress, newAddress); }
399
408 bool setDeviceAddress(uint8_t address, uint8_t newAddress);
409
415 uint16_t readModel() { return readModel(_destinationAddress); }
416
423 uint16_t readModel(uint8_t address);
424
429 uint16_t getModel() const { return _model; }
430
431 const char* getModelName() const { return getModelName(_model); }
432
438 static const char* getModelName(uint16_t model);
439
445 Mode readMode() { return _readMode(_destinationAddress, _model); }
446
453 Mode readMode(uint8_t address) { return _readMode(address, readModel(address)); }
454
461 bool setMode(Mode mode) { return _setMode(_destinationAddress, _model, mode); }
462
470 bool setMode(uint8_t address, Mode mode) { return _setMode(address, readModel(address), mode); }
471
477 bool read() { return _read(_destinationAddress, _model); }
478
485 bool read(uint8_t address) { return _read(address, readModel(address)); }
486
492 bool resetEnergy() { return resetEnergy(_destinationAddress); }
493
500 bool resetEnergy(uint8_t address);
501
502 // Try to change the baud rate of the JSY. Returns true if the baud rate was changed.
503 // This function is blocking until the change is confirmed or the timeout is reached.
504
511 bool setBaudRate(BaudRate baudRate) { return setBaudRate(_destinationAddress, baudRate); }
512
520 bool setBaudRate(uint8_t address, BaudRate baudRate);
521
522#ifdef MYCILA_JSON_SUPPORT
523 void toJson(const JsonObject& root) const;
524#endif
525
526 gpio_num_t getRXPin() const { return _pinRX; }
527 gpio_num_t getTXPin() const { return _pinTX; }
528 bool isEnabled() const { return _enabled; }
529 BaudRate getBaudRate() const { return _baudRate; }
530
535 uint8_t getDestinationAddress() const { return _destinationAddress; }
536
537 BaudRate getMinAvailableBaudRate() const;
538 static BaudRate getMinAvailableBaudRate(uint16_t model);
539
544 BaudRate getMaxAvailableBaudRate() const;
545 static BaudRate getMaxAvailableBaudRate(uint16_t model);
546
551 bool isBaudRateSupported(BaudRate baudRate) const;
552 static bool isBaudRateSupported(uint16_t model, BaudRate baudRate);
553
558 uint8_t getLastAddress() const { return _lastAddress; }
559
563 uint32_t getTime() const { return _time; }
564
565 // check if the device is connected to the grid, meaning if last read was successful
566 bool isConnected() const { return _data.aggregate.frequency > 0; }
567
568 void setCallback(Callback callback) { _callback = callback; }
569
570 private:
571 Callback _callback = nullptr;
572 gpio_num_t _pinRX = GPIO_NUM_NC;
573 gpio_num_t _pinTX = GPIO_NUM_NC;
574 HardwareSerial* _serial = nullptr;
575 std::mutex _mutex;
576 TaskHandle_t _taskHandle;
577 uint32_t _time = 0;
578 uint32_t _pause = MYCILA_JSY_ASYNC_READ_PAUSE_MS;
579 uint8_t _destinationAddress = MYCILA_JSY_ADDRESS_BROADCAST;
580 uint8_t _lastAddress = MYCILA_JSY_ADDRESS_UNKNOWN;
581 BaudRate _baudRate = BaudRate::UNKNOWN;
582 bool _enabled = false;
583 uint16_t _model = MYCILA_JSY_MK_UNKNOWN;
584 // buffer to read/write data
585 // biggest need is for JSY-MK-333: 102 registers of 2 bytes each + 5 bytes for the response: 209 bytes
586 // we use 14 blocks of 16 bytes: 224 bytes
587 uint8_t _buffer[224];
588 Data _data;
589
590 private:
591 enum class ReadResult {
592 READ_SUCCESS = 0,
593 READ_TIMEOUT,
594 READ_ERROR_COUNT,
595 READ_ERROR_CRC,
596 READ_ERROR_ADDRESS,
597 };
598
599 bool _set(uint8_t address, uint8_t newAddress, BaudRate newBaudRate);
600 bool _read(uint8_t address, uint16_t model);
601 Mode _readMode(uint8_t address, uint16_t model);
602 bool _setMode(uint8_t address, uint16_t model, Mode mode);
603
604 bool _canRead(uint8_t address, BaudRate baudRate);
605 ReadResult _timedRead(uint8_t expectedAddress, size_t expectedLen, BaudRate baudRate);
606 void _send(uint8_t address, size_t len);
607 size_t _drop();
608 void _openSerial(BaudRate baudRate);
609 BaudRate _detectBauds(uint8_t address);
610
611 static uint16_t _crc16(const uint8_t* buffer, size_t len);
612 static uint8_t _register8(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress, uint8_t index = 0);
613 static uint16_t _register16(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
614 static uint32_t _register32(const uint8_t* buffer, uint16_t registerStart, uint16_t registerSize, uint16_t registerAddress);
615 static void _jsyTask(void* pvParameters);
616 };
617} // namespace Mycila
Mycila::JSY::Data
Definition MycilaJSY.h:288
Mycila::JSY::Metrics
Definition MycilaJSY.h:122
Mycila::JSY::Metrics::resistance
float resistance() const
Compute the resistance of the load in ohms (R = P / I^2).
Definition MycilaJSYMetrics.cpp:16
Mycila::JSY::Metrics::reactivePower
float reactivePower
Reactive power in volt-amperes reactive (VAr). Can be positive or negative.
Definition MycilaJSY.h:166
Mycila::JSY::Metrics::phaseAngleU
float phaseAngleU
voltage phase angle in degrees (°).
Definition MycilaJSY.h:219
Mycila::JSY::Metrics::apparentPower
float apparentPower
Apparent power in volt-amperes (VA). Always positive.
Definition MycilaJSY.h:159
Mycila::JSY::Metrics::thdU
float thdU
total harmonic distortion of voltage (THDu), as a percentage (%).
Definition MycilaJSY.h:237
Mycila::JSY::Metrics::powerFactor
float powerFactor
Power factor. Positive value between 0 and 1.
Definition MycilaJSY.h:152
Mycila::JSY::Metrics::thdi
float thdi(float phi=0) const
Compute the total harmonic distortion percentage of current (THDi). This assumes THDu = 0 (perfect vo...
Definition MycilaJSYMetrics.cpp:9
Mycila::JSY::Metrics::nominalPower
float nominalPower() const
Compute the nominal power of the load in watts (P = V^2 / R).
Definition MycilaJSYMetrics.cpp:20
Mycila::JSY::Metrics::reactiveEnergy
uint32_t reactiveEnergy
Reactive energy in volt-amperes reactive-hours (VArh).
Definition MycilaJSY.h:193
Mycila::JSY::Metrics::activePower
float activePower
Active power in watts (W). Can be positive or negative.
Definition MycilaJSY.h:146
Mycila::JSY::Metrics::activeEnergyImported
uint32_t activeEnergyImported
Active energy imported in watt-hours (Wh), going to the load, when activePower > 0.
Definition MycilaJSY.h:180
Mycila::JSY::Metrics::current
float current
Current in amperes (A).
Definition MycilaJSY.h:140
Mycila::JSY::Metrics::activeEnergy
uint32_t activeEnergy
Active energy in watt-hours (Wh).
Definition MycilaJSY.h:173
Mycila::JSY::Metrics::phaseAngleI
float phaseAngleI
current phase angle in degrees (°).
Definition MycilaJSY.h:225
Mycila::JSY::Metrics::thdI
float thdI
total harmonic distortion of current (THDi) as a percentage (%).
Definition MycilaJSY.h:243
Mycila::JSY::Metrics::phaseAngleUI
float phaseAngleUI
voltage - current phase angle displacement in degrees (°).
Definition MycilaJSY.h:231
Mycila::JSY::Metrics::activeEnergyReturned
uint32_t activeEnergyReturned
Active energy returned in watt-hours (Wh), coming from the load, when activePower < 0.
Definition MycilaJSY.h:187
Mycila::JSY::Metrics::voltage
float voltage
Voltage in volts (V).
Definition MycilaJSY.h:134
Mycila::JSY::Metrics::reactiveEnergyReturned
uint32_t reactiveEnergyReturned
Reactive energy returned in volt-amperes reactive-hours (VArh), coming from the load,...
Definition MycilaJSY.h:207
Mycila::JSY::Metrics::reactiveEnergyImported
uint32_t reactiveEnergyImported
Reactive energy imported in volt-amperes reactive-hours (VArh), going to the load,...
Definition MycilaJSY.h:200
Mycila::JSY::Metrics::dimmedVoltage
float dimmedVoltage() const
Compute the dimmed voltage (V = P / I).
Definition MycilaJSYMetrics.cpp:18
Mycila::JSY::Metrics::frequency
float frequency
Frequency in hertz (Hz).
Definition MycilaJSY.h:128
Mycila::JSY::Metrics::apparentEnergy
uint32_t apparentEnergy
Apparent energy in volt-amperes-hours (VAh).
Definition MycilaJSY.h:213
Mycila::JSY
Definition MycilaJSY.h:85
Mycila::JSY::getModel
uint16_t getModel() const
Get the cached JSY model that was read during begin()
Definition MycilaJSY.h:429
Mycila::JSY::read
bool read()
Read the JSY values.
Definition MycilaJSY.h:477
Mycila::JSY::begin
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:352
Mycila::JSY::getLastAddress
uint8_t getLastAddress() const
Get the address of the last device's response.
Definition MycilaJSY.h:558
Mycila::JSY::setMode
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:461
Mycila::JSY::readMode
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:453
Mycila::JSY::setDeviceAddress
bool setDeviceAddress(uint8_t newAddress)
Set a new address for a device.
Definition MycilaJSY.h:398
Mycila::JSY::resetEnergy
bool resetEnergy()
Reset the energy counters of the JSY.
Definition MycilaJSY.h:492
Mycila::JSY::getTime
uint32_t getTime() const
Definition MycilaJSY.h:563
Mycila::JSY::setBaudRate
bool setBaudRate(BaudRate baudRate)
Change the baud rate of the JSY.
Definition MycilaJSY.h:511
Mycila::JSY::getMaxAvailableBaudRate
BaudRate getMaxAvailableBaudRate() const
Get the maximum available baud rate supported by the current JSY model connected.
Definition MycilaJSY.cpp:1297
Mycila::JSY::isBaudRateSupported
bool isBaudRateSupported(BaudRate baudRate) const
Check if a baud rate is supported by the current JSY model connected.
Definition MycilaJSY.cpp:1320
Mycila::JSY::read
bool read(uint8_t address)
Read the JSY values.
Definition MycilaJSY.h:485
Mycila::JSY::end
void end()
Ends the JSY communication.
Definition MycilaJSY.cpp:732
Mycila::JSY::setMode
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:470
Mycila::JSY::readMode
Mode readMode()
Reads the JSY mode (AC or DC). Some JSY are able to work with either AC or DC current.
Definition MycilaJSY.h:445
Mycila::JSY::readModel
uint16_t readModel()
Reads the JSY model.
Definition MycilaJSY.h:415
Mycila::JSY::getDestinationAddress
uint8_t getDestinationAddress() const
Get the address used to send requests.
Definition MycilaJSY.h:535