Add support for GM861

- Add support for GM861 1D and 2D bar code reader (#18399)
- Bump version to v12.5.0.2
This commit is contained in:
Theo Arends 2023-05-05 16:50:19 +02:00
parent 069278f966
commit d7b6d72e19
6 changed files with 358 additions and 15 deletions

View File

@ -215,6 +215,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_PROJECTOR_CTRL | - | - / - | - | - | - | - | | USE_PROJECTOR_CTRL | - | - / - | - | - | - | - |
| USE_AS608 | - | - / - | - | - | - | - | | USE_AS608 | - | - / - | - | - | - | - |
| USE_LD2410 | - | - / - | - | - | - | - | | USE_LD2410 | - | - / - | - | - | - | - |
| USE_GM861 | - | - / - | - | - | - | - |
| USE_TCP_BRIDGE | - | - / - | - | - | - | - | zbbridge / zbbrdgpro | USE_TCP_BRIDGE | - | - / - | - | - | - | - | zbbridge / zbbrdgpro
| | | | | | | | | | | | | | | |
| USE_NRF24 | - | - / - | - | - | - | - | | USE_NRF24 | - | - / - | - | - | - | - |

View File

@ -3,7 +3,25 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development ## [Unreleased] - Development
## [12.5.0.1] ## [12.5.0.2]
### Added
- Matter support for Shutters with Tilt
- Matter POC for remote Relay
- Support for Zero-Cross Dimmer on ESP32, changed calculation on EPS8266, high resolution control e.g. Solar: `ZCDimmerSet`
- ESP32 Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt.
- ESP32 `Shuttersetup` for "Shelly 2.5 pro" automatic calibration and setup (experimental)
- Berry `tcpclientasync` class for non-blocking TCP client
- Support for GM861 1D and 2D bar code reader (#18399)
### Breaking Changed
### Changed
### Fixed
### Removed
## [12.5.0.1] 20230505
### Added ### Added
- Matter sensors Humidity, Pressure, Illuminance; optimize memory (#18441) - Matter sensors Humidity, Pressure, Illuminance; optimize memory (#18441)
- Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control (#18386) - Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control (#18386)
@ -11,14 +29,6 @@ All notable changes to this project will be documented in this file.
- Matter UI to change endpoints configuration (#18498) - Matter UI to change endpoints configuration (#18498)
- Matter support for Shutters (without Tilt) (#18509) - Matter support for Shutters (without Tilt) (#18509)
- Support for TC74 temperature sensor by Michael Loftis (#18042) - Support for TC74 temperature sensor by Michael Loftis (#18042)
- Matter support for Shutters with Tilt
- Matter POC for remote Relay
- Added support for Zero-Cross Dimmer on ESP32, changed calculation on EPS8266, high resolution control e.g. Solar: `ZCDimmerSet`
- ESP32: Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt.
- ESP32: `Shuttersetup` for "Shelly 2.5 pro" automatic calibration and setup (experimental)
- Berry add `tcpclientasync` class for non-blocking TCP client
### Breaking Changed
### Changed ### Changed
- ESP32 Framework (Core) from v2.0.7 to v2.0.8 - ESP32 Framework (Core) from v2.0.7 to v2.0.8
@ -31,8 +41,6 @@ All notable changes to this project will be documented in this file.
- Berry fix rules for string comparisons (#18464) - Berry fix rules for string comparisons (#18464)
- Shutter: GarageMode does not stop on console commands, `ShutterSetOpen` and `ShutterSetClose` does not reset direction (#18539) - Shutter: GarageMode does not stop on console commands, `ShutterSetOpen` and `ShutterSetClose` does not reset direction (#18539)
### Removed
## [Released] ## [Released]
## [12.5.0] 20230417 ## [12.5.0] 20230417

View File

@ -110,14 +110,18 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
[Complete list](BUILDS.md) of available feature and sensors. [Complete list](BUILDS.md) of available feature and sensors.
## Changelog v12.5.0.1 ## Changelog v12.5.0.2
### Added ### Added
- Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control [#18386](https://github.com/arendst/Tasmota/issues/18386) - Command ``SetOption152 0/1`` to select two (0 = default) pin bistable or one (1) pin latching relay control [#18386](https://github.com/arendst/Tasmota/issues/18386)
- Support for TC74 temperature sensor by Michael Loftis [#18042](https://github.com/arendst/Tasmota/issues/18042) - Support for TC74 temperature sensor by Michael Loftis [#18042](https://github.com/arendst/Tasmota/issues/18042)
- Support for GM861 1D and 2D bar code reader [#18399](https://github.com/arendst/Tasmota/issues/18399)
- Matter sensors Humidity, Pressure, Illuminance [#18441](https://github.com/arendst/Tasmota/issues/18441) - Matter sensors Humidity, Pressure, Illuminance [#18441](https://github.com/arendst/Tasmota/issues/18441)
- Matter allow `Matter#Initialized` rule once the device is configured [#18451](https://github.com/arendst/Tasmota/issues/18451) - Matter allow `Matter#Initialized` rule once the device is configured [#18451](https://github.com/arendst/Tasmota/issues/18451)
- Matter UI to change endpoints configuration [#18498](https://github.com/arendst/Tasmota/issues/18498) - Matter UI to change endpoints configuration [#18498](https://github.com/arendst/Tasmota/issues/18498)
- Matter support for Shutters (without Tilt) [#18509](https://github.com/arendst/Tasmota/issues/18509) - Matter support for Shutters with Tilt [#18509](https://github.com/arendst/Tasmota/issues/18509)
- ESP32 Enhanced Shutterbuttons functionality to control tilt position, additionally incr/decr possible to position and tilt.
- ESP32 `Shuttersetup` for "Shelly 2.5 pro" automatic calibration and setup (experimental)
- Berry `tcpclientasync` class for non-blocking TCP client
### Breaking Changed ### Breaking Changed

View File

@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_ #ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_
const uint32_t VERSION = 0x0C050001; // 12.5.0.1 const uint32_t VERSION = 0x0C050002; // 12.5.0.2
#endif // _TASMOTA_VERSION_H_ #endif // _TASMOTA_VERSION_H_

View File

@ -831,7 +831,8 @@
// #define VINDRIKTNING_SHOW_PM1 // Display undocumented/supposed PM1.0 values // #define VINDRIKTNING_SHOW_PM1 // Display undocumented/supposed PM1.0 values
// #define VINDRIKTNING_SHOW_PM10 // Display undocumented/supposed PM10 values // #define VINDRIKTNING_SHOW_PM10 // Display undocumented/supposed PM10 values
//#define USE_LD2410 // Add support for HLK-LD2410 24GHz smart wave motion sensor (+2k8 code) //#define USE_LD2410 // Add support for HLK-LD2410 24GHz smart wave motion sensor (+2k8 code)
// #define USE_LOX_O2 // Add support for LuminOx LOX O2 Sensor (+0k8 code) //#define USE_LOX_O2 // Add support for LuminOx LOX O2 Sensor (+0k8 code)
//#define USE_GM861 // Add support for GM861 1D and 2D Bar Code Reader (+1k3 code)
// -- Power monitoring sensors -------------------- // -- Power monitoring sensors --------------------
#define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code) #define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code)

View File

@ -0,0 +1,329 @@
/*
xsns_107_gm861.ino - Support for GM861 Bar Code Reader for Tasmota
Copyright (C) 2023 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/>.
*/
#ifdef USE_GM861
/*********************************************************************************************\
* GM861 1D and 2D Bar Code Reader
*
* For background information see https://github.com/arendst/Tasmota/discussions/18399
\*********************************************************************************************/
#define XSNS_107 107
/*
#define GPIO_GM861_TX 304
#define D_SENSOR_GM861_TX "GM861 Tx"
#define GPIO_GM861_RX 305
#define D_SENSOR_GM861_RX "GM861 Rx"
Command ===================== Response ============== Description =========================================================
Headr Ty Ln Addrs Data Check Headr Ty Ln Data Check
----- -- -- ----- ----- ----- ----- -- -- ----- -----
7E 00 07 01 00 2A 02 D8 0F 02 00 00 02 39 01 C1 4C Get baudrate (9600)
7E 00 08 02 00 2A 39 01 A7 EA 02 00 00 02 39 01 SS SS Set baudrate to 9600
7E 00 08 01 00 00 D6 AB CD LED on, Mute off, Normal lighting, Normal brightness, Continuous mode
7E 00 08 01 00 02 01 AB CD 02 00 00 01 00 33 31 Command trigger Mode (Set bit0 of zone byte 0x0002)
7E 00 08 01 00 02 01 AB CD Trigger mode
7E 00 08 01 00 2C 02 AB CD Full area, Allow read all bar codes
7E 00 08 01 00 36 01 AB CD Allow read Code39
7E 00 08 01 00 37 00 AB CD Code39 Min length
7E 00 08 01 00 38 FF AB CD Code39 Max length
7E 00 08 01 00 B0 01 AB CD Cut out data:Output Start part
7E 00 08 01 00 B1 FF AB CD Cut out M bytes from start
7E 00 08 01 00 B0 02 AB CD Output End part
7E 00 08 01 00 B2 FF AB CD Cut out N bytes from end
7E 00 08 01 00 B0 03 AB CD Output center part
7E 00 08 01 00 B1 03 AB CD Cut out N bytes from start (Eg: three characters)
7E 00 08 01 00 B2 02 AB CD Cut out N bytes from end (Eg: two characters)
7E 00 08 01 00 D9 50 81 D3 02 00 00 01 00 33 31 Zone bytes reset to defaults
7E 00 08 01 00 D9 55 D1 76 02 00 00 01 00 33 31 Restore user-defined factory settings
7E 00 08 01 00 D9 56 E1 15 02 00 00 01 00 33 31 Save as user-defined factory settings
7E 00 08 01 00 D9 A5 3E 69 02 00 00 01 00 33 31 Deep sleep, can be awakened by serial port interrupt, this serial port command is invalid
7E 00 08 01 00 D9 00 DB 26 02 00 00 01 00 33 31 Wake up from sleep
7E 00 08 01 00 E7 00 AB CD OUT1 Output low level
7E 00 08 01 00 E7 01 AB CD OUT1 Output high level
7E 00 09 01 00 00 00 DE C8 02 00 00 01 00 33 31 Save settings to flash
7E 00 0A 01 00 00 00 30 1A 03 00 00 01 00 33 31 Send heartbeat every 10 seconds
*/
enum Gm861States {
GM861_STATE_DONE,
GM861_STATE_DUMP,
GM861_STATE_SERIAL_OUTPUT,
GM861_STATE_OUTPUT,
GM861_STATE_SETUP_CODE_ON,
GM861_STATE_INIT_OFFSET = 16, // Init after (GM861_STATE_INIT_OFFSET - GM861_STATE_SETUP_CODE_ON) * 0.25 seconds restart
GM861_STATE_RESET
};
#include <TasmotaSerial.h>
TasmotaSerial *Gm861Serial = nullptr;
struct GDK {
char barcode[30];
uint8_t index;
uint8_t state;
bool heartbeat;
bool read;
} Gm861;
/*********************************************************************************************/
uint32_t Gm861Crc(uint8_t* ptr, uint32_t len) {
// When no need for checking CRC, CRC byte can be filled in 0xAB 0xCD
uint32_t crc = 0;
while (len-- != 0) {
for (uint8_t i = 0x80; i != 0; i /= 2) {
crc *= 2;
if ((crc & 0x10000) !=0) { // After multiplying the last bit of CRC by 2, if the first bit is 1, then divide by 0x11021
crc ^= 0x11021;
}
if ((*ptr & i) != 0) { // If this bit is 1, then CRC = previous CRC + this bit/CRC_CCITT
crc ^= 0x1021;
}
}
ptr++;
}
return crc;
}
void Gm861Send(uint32_t type, uint32_t len, uint32_t address, uint32_t data) {
uint8_t buffer[10];
buffer[0] = 0x7E;
buffer[1] = 0;
buffer[2] = type;
buffer[3] = len;
buffer[4] = 0;
buffer[5] = address;
buffer[6] = data;
uint8_t index = 7;
if (len > 1) {
buffer[7] = data >> 8;
index++;
}
uint32_t crc = Gm861Crc(buffer+2, len + 4);
buffer[index] = crc >> 8;
index++;
buffer[index] = crc;
index++;
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)buffer, index);
Gm861Serial->write(buffer, index);
}
void Gm861SetZone(uint32_t address, uint32_t data) {
Gm861.read = false;
uint32_t len = 1;
if (0x2A == address) { len = 2; } // Baudrate
Gm861Send(8, len, address, data);
}
void Gm861GetZone(uint32_t address) {
Gm861.read = true;
Gm861.index = address;
uint32_t data = 1;
if (0x2A == address) { data = 2; } // Baudrate
Gm861Send(7, 1, address, data);
}
void Gm861SerialInput(void) {
if (!Gm861Serial->available()) { return; }
char buffer[256];
uint32_t byte_counter = 0;
// Use timeout to allow large serial reads within a window
uint32_t timeout = millis();
while (millis() - 10 < timeout) {
if (Gm861Serial->available()) {
timeout = millis();
buffer[byte_counter++] = Gm861Serial->read();
if (byte_counter >= sizeof(buffer) -1) { break; }
}
}
buffer[byte_counter] = 0; // Add string terminating zero
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)buffer, byte_counter);
if (0 == buffer[1]) { // Command result or Heartbeat
if (2 == buffer[0]) { // Command result
// 02 00 00 01 00 33 31 - Command acknowledge
// 02 00 00 01 01 23 10 - Command result (Zonebyte 96 - 0x60)
// 02 00 00 02 39 01 C1 4C - Command result (Zonebytes 42/43 - 0x2A)
if (Gm861.read) {
uint32_t result = buffer[4];
if (2 == buffer[3]) { // Length
result += (buffer[5] << 8);
}
Gm861.read = false;
Response_P(S_JSON_COMMAND_INDEX_NVALUE, PSTR("GM861Zone"), Gm861.index, result);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR("GM861Zone"));
}
}
else if (3 == buffer[0]) { // Heartbeat
// 03 00 00 01 00 33 31 - Heartbeat response
// 03 00 0A 30 33 32 31 35 36 33 38 34 30 - Scan response with zone byte 96 = 0x81
Gm861.heartbeat = true;
}
} else { // Bar code
// 38 37 31 31 32 31 38 39 37 32 38 37 35 0D - Barcode 8711218972875
RemoveControlCharacter(buffer); // Remove control character (0x00 .. 0x1F and 0x7F)
snprintf_P(Gm861.barcode, sizeof(Gm861.barcode) -3, PSTR("%s"), buffer);
if (strlen(buffer) > sizeof(Gm861.barcode) -3) {
strcat(Gm861.barcode, "...");
}
ResponseTime_P(PSTR(",\"GM861\":\"%s\"}"), buffer);
MqttPublishTeleSensor();
}
}
/********************************************************************************************/
void Gm861Init(void) {
if (PinUsed(GPIO_GM861_RX) && PinUsed(GPIO_GM861_TX)) {
Gm861Serial = new TasmotaSerial(Pin(GPIO_GM861_RX), Pin(GPIO_GM861_TX), 1);
if (Gm861Serial->begin(9600)) {
if (Gm861Serial->hardwareSerial()) {
ClaimSerial();
}
Gm861.barcode[0] = '0'; // No barcode yet
Gm861.state = GM861_STATE_INIT_OFFSET;
// AddLog(LOG_LEVEL_INFO, PSTR("GM8: Connected"));
}
}
}
void Gm861StateMachine(void) {
/*
Power on
14:25:04.219-025 DMP: 02 00 00 62 D6 00 20 00 00 0A 32 01 2C 00 87 3C 01 A1 1C 32 03 00 80 00 06 00 00 00 00 00 00 00 00 00 00 00 00 02 80 3C 00 00 00 06 00 00 39 01 05 64 0D 0D 0D 01 0D 01 04 80 09 04 80 05 04 80 01 04 80 01 08 04 80 08 04 80 08 04 80 08 04 80 08 01 80 00 00 00 04 80 01 01 00 00 00 00 04 80 00 00 03 00 01 00 30 FE
Default: setup code on
12:12:18.672-024 DMP: 02 00 00 61 D6 00 20 00 00 0A 32 01 2C 00 87 3C 01 A1 1C 32 03 00 80 00 06 00 00 00 00 00 00 00 00 00 00 00 00 02 80 3C 00 00 00 06 00 00 39 01 05 64 0D 0D 0D 01 0D 01 04 80 09 04 80 05 04 80 01 04 80 01 08 04 80 08 04 80 08 04 80 08 04 80 08 01 80 00 00 00 04 80 01 01 00 00 00 00 04 80 00 00 03 00 01 00 30 FE
Output:
14:37:45.129-027 DMP: 02 00 00 62 D6 00 20 01 00 0A 32 01 2C 00 87 3C 01 A1 1C 32 03 00 80 00 06 00 00 00 00 00 00 00 00 00 00 00 00 02 80 3C 00 00 00 06 00 00 39 01 05 64 0D 0D 0D 01 0D 01 04 80 09 04 80 05 04 80 01 04 80 01 08 04 80 08 04 80 08 04 80 08 04 80 08 01 80 00 00 00 04 80 01 01 00 00 00 00 04 80 00 00 03 00 01 00 20 91
Serial output:
14:39:04.887-027 DMP: 02 00 00 62 D6 00 20 01 00 0A 32 01 2C 00 87 3C 01 A0 1C 32 03 00 80 00 06 00 00 00 00 00 00 00 00 00 00 00 00 02 80 3C 00 00 00 06 00 00 39 01 05 64 0D 0D 0D 01 0D 01 04 80 09 04 80 05 04 80 01 04 80 01 08 04 80 08 04 80 08 04 80 08 04 80 08 01 80 00 00 00 04 80 01 01 00 00 00 00 04 80 00 00 03 00 01 00 2D 9E
*/
if (!Gm861.state) { return; }
switch (Gm861.state) {
case GM861_STATE_RESET:
Gm861SetZone(0xD9, 0x50); // Factory reset
Gm861.state = GM861_STATE_SETUP_CODE_ON +7; // Add time for reset to complete
break;
case GM861_STATE_SETUP_CODE_ON:
Gm861SetZone(0x00, 0xD6); // Set LED on, Mute off, Normal lighting, Normal brightness, Continuous mode (Default: setup code on)
break;
case GM861_STATE_OUTPUT:
Gm861SetZone(0x03, 0x01); // Enable output (Output)
break;
case GM861_STATE_SERIAL_OUTPUT:
Gm861SetZone(0x0D, 0xA0); // Enable serial port output (Serial Output)
break;
case GM861_STATE_DUMP:
Gm861Send(7, 1, 0, 0x62); // Dump zone bytes 0 to 97
AddLog(LOG_LEVEL_INFO, PSTR("GM8: Initialized"));
break;
}
Gm861.state--;
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
const char kGm861Commands[] PROGMEM = "GM861|" // Prefix
"Zone|Reset|Dump";
void (* const Gm861Command[])(void) PROGMEM = {
&CmndGm816Zone, &CmndGm816Reset, &CmndGm816Dump };
void CmndGm816Zone(void) {
// GM861Zone42 - Read baudrate
// GM861Zone0 0xD6 - Set LED on, Mute off, Normal lighting, Normal brightness, Continuous mode
if ((XdrvMailbox.index >= 0x00) && (XdrvMailbox.index <= 0xD9)) {
if ((XdrvMailbox.payload >= 0x00) && (XdrvMailbox.payload <= 0x09C4)) {
Gm861SetZone(XdrvMailbox.index, XdrvMailbox.payload);
ResponseCmndIdxNumber(XdrvMailbox.payload);
} else {
Gm861GetZone(XdrvMailbox.index);
ResponseClear();
}
}
}
void CmndGm816Reset(void) {
// GM861Reset 1 - Do factory reset and inititalize for serial comms
if (1 == XdrvMailbox.payload) {
Gm861.state = GM861_STATE_RESET;
ResponseCmndDone();
}
}
void CmndGm816Dump(void) {
// GM861Dump - Dump zone bytes 0 to 97. Needs logging level 4
Gm861Send(7, 1, 0, 0x62); // Dump zone bytes 0 to 97
ResponseCmndDone();
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
#ifdef USE_WEBSERVER
void Gm861Show(void) {
WSContentSend_PD(PSTR("{s}GM816{m}%s{e}"), Gm861.barcode);
}
#endif // USE_WEBSERVER
/*********************************************************************************************\
Interface
\*********************************************************************************************/
bool Xsns107(uint32_t function) {
bool result = false;
if (FUNC_INIT == function) {
Gm861Init();
}
else if (Gm861Serial) {
switch (function) {
case FUNC_LOOP:
case FUNC_SLEEP_LOOP:
Gm861SerialInput();
break;
case FUNC_EVERY_250_MSECOND:
Gm861StateMachine();
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
Gm861Show();
break;
#endif // USE_WEBSERVER
case FUNC_COMMAND:
result = DecodeCommand(kGm861Commands, Gm861Command);
break;
}
}
return result;
}
#endif // USE_GM861