mirror of https://github.com/arendst/Tasmota.git
247 lines
8.0 KiB
C++
247 lines
8.0 KiB
C++
/*
|
|
xsns_86_tfminiplus.ino - TFmini Plus interface for Tasmota
|
|
|
|
Copyright (C) 2021 Raphael Breiting and 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_TFMINIPLUS
|
|
/*****************************************************************************\
|
|
* TFmini, TFmini Plus, TFmini Plus (Indoor Version), TFmini-S - LiDAR Module
|
|
* Manufacturer: Benewake (Beijing) Co. Ltd.
|
|
*
|
|
* Code for Time-Of-Flight (TOF) family single-point short-range LiDAR sensor
|
|
* modules with UART interface.
|
|
*
|
|
* Connection Description for GH1.25-4P (Molex51021-0400) connector:
|
|
* No Color Corresponding PIN Function Comment
|
|
* 1 RED PIN-1 +5V Power Supply
|
|
* 2 WHITE PIN-2 RxD/SDA Receiving/Data
|
|
* 3 GREEN PIN-3 TxD/SCL Transmitting/Clock
|
|
* 4 BLACK PIN-4 GND Ground
|
|
*
|
|
* Before connecting module to Tasmota, please prepare module with
|
|
* the following sequence through terminal program (e.g. Termite):
|
|
* 1. Connect sensor to USB-to-UART (TTL 3.3V)
|
|
* 2. Set terminal to 115200bps and 8N1
|
|
* 3. Set frame rate to 0 (zero):
|
|
* send -->: 5A0603000000
|
|
* receive <-- (OK): 5A0603000000
|
|
* 4. Set baud rate to 9600bps
|
|
* send -->: 5A0806802500000D
|
|
* receive <-- (OK): 5A0806802500000D
|
|
* 5. Set terminal to 9600bps and 8N1
|
|
* 6. Save settings
|
|
* send -->: 5A04116F
|
|
* receive <-- (OK): 5A05110070
|
|
* receive <-- (FAIL): 5A05110071
|
|
* 7. When point 6 returned OK, than ready to connect to Tasmota! :)
|
|
* When point 6 returned FAIL, start with item 1 and
|
|
* - check connection of RX and TX pin are not exchanged
|
|
* - check power supply
|
|
* - 5V +-0,5V
|
|
* - 110mA average
|
|
* - 140mA max peak
|
|
*
|
|
* References:
|
|
* - http://en.benewake.com/product
|
|
* - https://de.aliexpress.com/item/32852024277.html?spm=a2g0s.9042311.0.0.27424c4d5Edizk
|
|
* - https://de.aliexpress.com/item/4001076614996.html?spm=a2g0s.9042311.0.0.27424c4d5Edizk
|
|
\*****************************************************************************/
|
|
|
|
#define XSNS_86 86
|
|
|
|
// Default baudrate
|
|
#define TFMP_SPEED 9600
|
|
|
|
// Serial buffer length for incoming data
|
|
#define TFMP_MAX_DATA_LEN 9
|
|
|
|
#include <TasmotaSerial.h>
|
|
|
|
char Tfmp_buffer[TFMP_MAX_DATA_LEN + 1];
|
|
|
|
struct xsns_86_tfminiplus
|
|
{
|
|
bool ready = false;
|
|
uint16_t distance = 0;
|
|
uint16_t sigstrength = 0;
|
|
uint16_t chiptemp = 0;
|
|
} tfminiplus_sensor;
|
|
|
|
// Software and hardware serial pointers
|
|
TasmotaSerial *TfmpSerial = nullptr;
|
|
|
|
void TfmpInit(void)
|
|
{
|
|
if (!tfminiplus_sensor.ready)
|
|
{
|
|
if (PinUsed(GPIO_TFMINIPLUS_RX) && PinUsed(GPIO_TFMINIPLUS_TX))
|
|
{
|
|
TfmpSerial = new TasmotaSerial(Pin(GPIO_TFMINIPLUS_RX), Pin(GPIO_TFMINIPLUS_TX), 1);
|
|
if (TfmpSerial->begin(TFMP_SPEED))
|
|
{
|
|
if (TfmpSerial->hardwareSerial())
|
|
{
|
|
ClaimSerial();
|
|
}
|
|
tfminiplus_sensor.ready = true;
|
|
TfmpSerial->flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TfmpTrigger(void)
|
|
{
|
|
if (TfmpSerial && tfminiplus_sensor.ready)
|
|
{
|
|
TfmpSerial->write(0x5A);
|
|
TfmpSerial->write(0x04);
|
|
TfmpSerial->write(0x04);
|
|
TfmpSerial->write(0x62);
|
|
}
|
|
}
|
|
|
|
void TfmpProcessData(void) {
|
|
// check crc sum
|
|
uint16_t crc = 0;
|
|
for (int i = 0; i < TFMP_MAX_DATA_LEN - 1; ++i) {
|
|
crc += (uint16_t)Tfmp_buffer[i];
|
|
}
|
|
|
|
if ((char)(crc & 0xff) == Tfmp_buffer[TFMP_MAX_DATA_LEN-1]) {
|
|
// distance to object (default in cm)
|
|
tfminiplus_sensor.distance = (Tfmp_buffer[3] << 8) + Tfmp_buffer[2];
|
|
// signal strength (between 100 and 65535 fine, otherwise 0 due to out of range of non-reflective surface)
|
|
tfminiplus_sensor.sigstrength = (Tfmp_buffer[5] << 8) + Tfmp_buffer[4];
|
|
// chip temperature
|
|
tfminiplus_sensor.chiptemp = (((Tfmp_buffer[7] << 8) + Tfmp_buffer[6]) >> 3) - 256;
|
|
|
|
DEBUG_SENSOR_LOG(PSTR("TFmini Plus: Distance: %d"), tfminiplus_sensor.distance);
|
|
DEBUG_SENSOR_LOG(PSTR("TFmini Plus: Signal: %d"), tfminiplus_sensor.sigstrength);
|
|
DEBUG_SENSOR_LOG(PSTR("TFmini Plus: Chip Temp: %d"), tfminiplus_sensor.chiptemp);
|
|
} else {
|
|
DEBUG_SENSOR_LOG(PSTR("TFmini Plus: crc error"));
|
|
}
|
|
TfmpSerial->flush();
|
|
}
|
|
|
|
void TfmpProcessSerialData (void)
|
|
{
|
|
uint8_t data;
|
|
bool dataReady;
|
|
if (TfmpSerial && tfminiplus_sensor.ready)
|
|
{
|
|
while (TfmpSerial->available() > 0)
|
|
{
|
|
data = TfmpSerial->read();
|
|
dataReady = TfmpAddData((char)data);
|
|
if (dataReady)
|
|
{
|
|
TfmpProcessData();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TfmpAddData(char nextChar)
|
|
{
|
|
// Buffer position
|
|
static uint8_t currentIndex = 0;
|
|
// Store data into buffer at position
|
|
if ((currentIndex >0) && (0x59 == Tfmp_buffer[currentIndex-1]) && (0x59 == nextChar))
|
|
{
|
|
currentIndex = 1;
|
|
}
|
|
Tfmp_buffer[currentIndex] = nextChar;
|
|
currentIndex++;
|
|
// Check for too many data
|
|
if (currentIndex > TFMP_MAX_DATA_LEN)
|
|
{
|
|
// Terminate buffer and reset position
|
|
Tfmp_buffer[TFMP_MAX_DATA_LEN] = '\0';
|
|
currentIndex = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef USE_WEBSERVER
|
|
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
|
const char HTTP_SNS_SIGNALSTRENGTH[] PROGMEM = "{s}%s " D_SIGNALSTRENGTH "{m}%d{e}";
|
|
const char HTTP_SNS_CHIPTEMPERATURE[] PROGMEM = "{s}%s " D_CHIPTEMPERATURE "{m}%d " D_UNIT_DEGREE "%c{e}";
|
|
#endif // USE_WEBSERVER
|
|
|
|
void TfmpShow(bool json)
|
|
{
|
|
char sensor_name[12];
|
|
strcpy_P(sensor_name, "TFminiPlus");
|
|
char distance_chr[FLOATSZ];
|
|
dtostrfd(tfminiplus_sensor.distance, 3, distance_chr);
|
|
|
|
if (json) {
|
|
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_DISTANCE "\":\"%s\",\"" D_JSON_SIGNALSTRENGTH "\":\"%d\",\"" D_JSON_CHIPTEMPERATURE "\":%d}"),
|
|
sensor_name, distance_chr, tfminiplus_sensor.sigstrength, tfminiplus_sensor.chiptemp);
|
|
#ifdef USE_DOMOTICZ
|
|
if (0 == TasmotaGlobal.tele_period) {
|
|
DomoticzSensor(DZ_COUNT, distance_chr);
|
|
}
|
|
#endif // USE_DOMOTICZ
|
|
#ifdef USE_WEBSERVER
|
|
} else {
|
|
WSContentSend_P(HTTP_SNS_DISTANCE_CM, sensor_name, distance_chr);
|
|
WSContentSend_P(HTTP_SNS_SIGNALSTRENGTH, sensor_name, tfminiplus_sensor.sigstrength);
|
|
WSContentSend_P(HTTP_SNS_CHIPTEMPERATURE, sensor_name, tfminiplus_sensor.chiptemp, TempUnit());
|
|
#endif // USE_WEBSERVER
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Interface
|
|
\*********************************************************************************************/
|
|
|
|
bool Xsns86(byte callback_id)
|
|
{
|
|
bool result = false;
|
|
if (FUNC_INIT == callback_id)
|
|
{
|
|
TfmpInit();
|
|
}
|
|
else if (TfmpSerial && tfminiplus_sensor.ready)
|
|
{
|
|
switch (callback_id) {
|
|
case FUNC_EVERY_SECOND:
|
|
TfmpTrigger();
|
|
TfmpProcessSerialData();
|
|
result = true;
|
|
break;
|
|
case FUNC_JSON_APPEND:
|
|
TfmpShow(1);
|
|
break;
|
|
#ifdef USE_WEBSERVER
|
|
case FUNC_WEB_SENSOR:
|
|
TfmpShow(0);
|
|
break;
|
|
#endif // USE_WEBSERVER
|
|
case FUNC_SAVE_BEFORE_RESTART:
|
|
break;
|
|
case FUNC_COMMAND:
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#endif // USE_TFMINIPLUS
|