Merge branches 'development' and 'development' of https://github.com/arendst/Sonoff-Tasmota into development

This commit is contained in:
localhost61 2019-03-16 01:50:31 +01:00
commit f77280d1bb
94 changed files with 1734 additions and 7571 deletions

2
API.md
View File

@ -23,6 +23,8 @@ FUNC_JSON_APPEND | | | | x | | Extend tele
FUNC_WEB_APPEND | | | | x | | Extend webgui ajax info
FUNC_SAVE_BEFORE_RESTART | | | | x | | Just before a planned restart
FUNC_COMMAND | x | | x | x | | When a command is not recognized
FUNC_COMMAND_DRIVER | x | 6.4.1.21 | x | | | When command Driver\<id\> is executed
FUNC_COMMAND_SENSOR | x | 6.4.1.21 | | x | | When command Sensor\<id\> is executed
FUNC_MQTT_SUBSCRIBE | | 5.12.0k | x | | | At end of MQTT subscriptions
FUNC_MQTT_INIT | | 5.12.0k | x | | | Once at end of MQTT connection
FUNC_MQTT_DATA | x | 5.12.0k | x | | | Before decoding command

View File

@ -129,7 +129,6 @@ Libraries used with Sonoff-Tasmota are:
- [Adafruit SSD1306](https://github.com/adafruit/Adafruit_SSD1306)
- [Adafruit GFX](https://github.com/adafruit/Adafruit-GFX-Library)
- [ArduinoJson](https://arduinojson.org/)
- [arduino mqtt](https://github.com/256dpi/arduino-mqtt)
- [Bosch BME680](https://github.com/BoschSensortec/BME680_driver)
- [C2 Programmer](http://app.cear.ufpb.br/~lucas.hartmann/tag/efm8bb1/)
- [esp-epaper-29-ws-20171230-gemu](https://github.com/gemu2015/Sonoff-Tasmota/tree/displays/lib)

View File

@ -0,0 +1,653 @@
/*
# Copyright (c) 2019 Frogmore42
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <Wire.h>
#include <math.h>
#include <stdio.h>
#include <twi.h>
#include <FrogmoreScd30.h>
#define COMMAND_SCD30_CONTINUOUS_MEASUREMENT 0x0010
#define COMMAND_SCD30_MEASUREMENT_INTERVAL 0x4600
#define COMMAND_SCD30_GET_DATA_READY 0x0202
#define COMMAND_SCD30_READ_MEASUREMENT 0x0300
#define COMMAND_SCD30_CALIBRATION_TYPE 0x5306
#define COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR 0x5204
#define COMMAND_SCD30_TEMPERATURE_OFFSET 0x5403
#define COMMAND_SCD30_ALTITUDE_COMPENSATION 0x5102
#define COMMAND_SCD30_SOFT_RESET 0xD304
#define COMMAND_SCD30_GET_FW_VERSION 0xD100
#define COMMAND_SCD30_STOP_MEASUREMENT 0x0104
#define SCD30_DATA_REGISTER_BYTES 2
#define SCD30_DATA_REGISTER_WITH_CRC 3
#define SCD30_MEAS_BYTES 18
#ifdef SCD30_DEBUG
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL};
char scd30log_data[180];
#endif
void FrogmoreScd30::begin(TwoWire *pWire, uint8_t i2cAddress)
{
this->i2cAddress = i2cAddress;
if (pWire == NULL)
{
this->pWire = &Wire;
}
else
{
this->pWire = pWire;
}
co2NewDataLocation = -1; // indicates there is no data, so the 1st data point needs to fill up the median filter
this->pWire->setClockStretchLimit(200000);
this->ambientPressure = 0;
}
void FrogmoreScd30::begin(uint8_t i2cAddress)
{
begin(NULL, i2cAddress);
}
void FrogmoreScd30::begin(TwoWire *pWire)
{
begin(pWire, SCD30_ADDRESS);
}
void FrogmoreScd30::begin(void)
{
begin(NULL, SCD30_ADDRESS);
}
/*---------------------------------------------------------------------------
Function : opt_med5() In : pointer to array of 5 values
Out : a uint16_t which is the middle value of the sorted array
Job : optimized search of the median of 5 values
Notice : found on sci.image.processing cannot go faster unless assumptions are made on the nature of the input signal.
---------------------------------------------------------------------------*/
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { uint16_t temp=(a);(a)=(b);(b)=temp; }
uint16_t opt_med5(uint16_t * p)
{
PIX_SORT(p[0], p[1]);
PIX_SORT(p[3], p[4]);
PIX_SORT(p[0], p[3]);
PIX_SORT(p[1], p[4]);
PIX_SORT(p[1], p[2]);
PIX_SORT(p[2], p[3]);
PIX_SORT(p[1], p[2]);
return(p[2]);
}
// twi_status() attempts to read out any data left that is holding SDA low, so a new transaction can take place
// something like (http://www.forward.com.au/pfod/ArduinoProgramming/I2C_ClearBus/index.html)
int FrogmoreScd30::clearI2CBus(void)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "clearI2CBus");
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
return (twi_status());
}
#ifdef SCD30_DEBUG
void FrogmoreScd30::AddLog(uint8_t loglevel)
{
if (loglevel <= LOG_LEVEL_INFO)
{
Serial.printf("%s\r\n", scd30log_data);
}
}
#endif
uint8_t FrogmoreScd30::computeCRC8(uint8_t data[], uint8_t len)
// Computes the CRC that the SCD30 uses
{
uint8_t crc = 0xFF; //Init with 0xFF
for (uint8_t x = 0 ; x < len ; x++)
{
crc ^= data[x]; // XOR-in the next input byte
for (uint8_t i = 0 ; i < 8 ; i++)
{
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ 0x31);
else
crc <<= 1;
}
}
return crc; //No output reflection
}
// Sends stream of bytes to device
int FrogmoreScd30::sendBytes(void *pInput, uint8_t len)
{
uint8_t *pBytes = (uint8_t *) pInput;
int result;
uint8_t errorBytes = 0; // number of bytes that had an error in transmission
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendBytes: data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", pBytes[0], pBytes[1], pBytes[2], pBytes[3], pBytes[4], pBytes[5], pBytes[6], pBytes[7], pBytes[8]);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
pWire->beginTransmission(this->i2cAddress);
errorBytes = len - (pWire->write(pBytes, len));
result = pWire->endTransmission();
if (errorBytes || result)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendBytes: errorBytes: %d | Wire.end: %d", errorBytes, result);
AddLog(LOG_LEVEL_INFO);
#endif
}
result <<= 8; // leave room for error bytes number
result |= errorBytes; // low byte has number of bytes that were not written correctly
return (result);
}
// Gets a number of bytes from device
int FrogmoreScd30::getBytes(void *pOutput, uint8_t len)
{
uint8_t *pBytes = (uint8_t *) pOutput;
uint8_t result;
result = pWire->requestFrom(this->i2cAddress, len);
if (len != result)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30GetBytes: wire request expected %d got: %d", len, result);
AddLog(LOG_LEVEL_INFO);
#endif
return (ERROR_SCD30_NOT_ENOUGH_BYTES_ERROR);
}
if (pWire->available())
{
for (int x = 0; x < len; x++)
{
pBytes[x] = pWire->read();
}
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30GetBytes: data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", pBytes[0], pBytes[1], pBytes[2], pBytes[3], pBytes[4], pBytes[5], pBytes[6], pBytes[7], pBytes[8]);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
return (ERROR_SCD30_NO_ERROR);
}
return (ERROR_SCD30_UNKNOWN_ERROR);
}
//Sends just a command, no arguments, no CRC
int FrogmoreScd30::sendCommand(uint16_t command)
{
uint8_t data[2];
data[0] = command >> 8;
data[1] = command & 0xFF;
int error = sendBytes(data, sizeof(data));
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendCommand: Scd30SendBytes failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
}
return (error);
}
//Sends a command along with arguments and CRC
int FrogmoreScd30::sendCommandArguments(uint16_t command, uint16_t arguments)
{
uint8_t data[5];
data[0] = command >> 8;
data[1] = command & 0xFF;
data[2] = arguments >> 8;
data[3] = arguments & 0xFF;
data[4] = computeCRC8(&data[2], 2); //Calc CRC on the arguments only, not the command
int error = sendBytes(data, sizeof(data));
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendCommandArguments: Scd30SendBytes failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
}
return (error);
}
int FrogmoreScd30::get16BitRegCheckCRC(void* pInput, uint16_t *pData)
{
uint8_t *pBytes = (uint8_t *) pInput;
uint8_t expectedCRC = computeCRC8(pBytes, SCD30_DATA_REGISTER_BYTES);
if (expectedCRC != pBytes[SCD30_DATA_REGISTER_BYTES])
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30get16BitRegCheckCRC: expected: 0x%02X, but got: 0x%02X", expectedCRC, pBytes[SCD30_DATA_REGISTER_BYTES]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30get16BitRegCheckCRC: data: 0x%02X, 0x%02X, 0x%02X", pBytes[0], pBytes[1], pBytes[2]);
AddLog(LOG_LEVEL_INFO);
#endif
return (ERROR_SCD30_CRC_ERROR);
}
*pData = (uint16_t) pBytes[0] << 8 | pBytes[1]; // data from SCD30 is Big-Endian
return (ERROR_SCD30_NO_ERROR);
}
// gets 32 bits, (2) 16-bit chunks, and validates the CRCs
//
int FrogmoreScd30::get32BitRegCheckCRC(void *pInput, float *pData)
{
uint16_t tempU16High;
uint16_t tempU16Low;
uint8_t *pBytes = (uint8_t *) pInput;
uint32_t rawInt = 0;
int error = get16BitRegCheckCRC(pBytes, &tempU16High);
if (error) {
return (error);
}
error = get16BitRegCheckCRC(pBytes + SCD30_DATA_REGISTER_WITH_CRC, &tempU16Low);
if (error) {
return (error);
}
// data from SCD is Big-Endian
rawInt |= tempU16High;
rawInt <<= 16;
rawInt |= tempU16Low;
*pData = * (float *) &rawInt;
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "get32BitRegCheckCRC: got: tempUs 0x%lX, %lX", tempU16High, tempU16Low);
AddLog(LOG_LEVEL_DEBUG);
#endif
if (isnan(*pData) || isinf(*pData))
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "get32BitRegCheckCRC: not a floating point number: rawInt 0x%lX", rawInt);
AddLog(LOG_LEVEL_INFO);
#endif
return (ERROR_SCD30_NOT_A_NUMBER_ERROR);
}
return (ERROR_SCD30_NO_ERROR);
}
//Gets two bytes (and check CRC) from SCD30
int FrogmoreScd30::readRegister(uint16_t registerAddress, uint16_t* pData)
{
int error = sendCommand(registerAddress);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: SendCommand error: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
delay(1); // the SCD30 uses clock streching to give it time to prepare data, waiting here makes it work
uint8_t data[SCD30_DATA_REGISTER_WITH_CRC];
error = getBytes(data, sizeof(data));
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: Scd30GetBytes error: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
uint16 regValue;
error = get16BitRegCheckCRC(data, &regValue);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: Scd30get16BitRegCheckCRC error: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
*pData = regValue;
return (ERROR_SCD30_NO_ERROR);
}
int FrogmoreScd30::softReset(void)
{
return (sendCommand(COMMAND_SCD30_SOFT_RESET));
}
int FrogmoreScd30::getAltitudeCompensation(uint16_t *pHeight_meter)
{
return (readRegister(COMMAND_SCD30_ALTITUDE_COMPENSATION, pHeight_meter));
}
int FrogmoreScd30::getAmbientPressure(uint16_t *pAirPressure_mbar)
{
*pAirPressure_mbar = ambientPressure;
return (ERROR_SCD30_NO_ERROR);
}
int FrogmoreScd30::getCalibrationType(uint16_t *pIsAuto)
{
uint16_t value = 0;
int error = readRegister(COMMAND_SCD30_CALIBRATION_TYPE, &value);
if (!error)
{
*pIsAuto = value != 0;
}
return (error);
}
int FrogmoreScd30::getFirmwareVersion(uint8_t *pMajor, uint8_t *pMinor)
{
uint16_t value;
int error = readRegister(COMMAND_SCD30_GET_FW_VERSION, &value);
if (!error)
{
*pMajor = value >> 8;
*pMinor = value & 0xFF;
}
return (error);
}
int FrogmoreScd30::getForcedRecalibrationFactor(uint16_t *pCo2_ppm)
{
return (readRegister(COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR, pCo2_ppm));
}
int FrogmoreScd30::getMeasurementInterval(uint16_t *pTime_sec)
{
return (readRegister(COMMAND_SCD30_MEASUREMENT_INTERVAL, pTime_sec));
}
int FrogmoreScd30::getTemperatureOffset(float *pOffset_degC)
{
uint16_t value;
int error = readRegister(COMMAND_SCD30_TEMPERATURE_OFFSET, &value);
if (!error)
{
// result is in centi-degrees, need to convert to degrees
*pOffset_degC = (float) value / 100.0;
}
return (error);
}
int FrogmoreScd30::getTemperatureOffset(uint16_t *pOffset_centiDegC)
{
uint16_t value;
int error = readRegister(COMMAND_SCD30_TEMPERATURE_OFFSET, &value);
if (!error)
{
// result is in centi-degrees, need to convert to degrees
*pOffset_centiDegC = value;
}
return (error);
}
int FrogmoreScd30::setAltitudeCompensation(uint16_t height_meter)
{
return (sendCommandArguments(COMMAND_SCD30_ALTITUDE_COMPENSATION, height_meter));
}
int FrogmoreScd30::setAmbientPressure(uint16_t airPressure_mbar)
{
ambientPressure = airPressure_mbar;
return (beginMeasuring(ambientPressure));
}
int FrogmoreScd30::setAutoSelfCalibration(void)
{
bool isAuto = true;
return (setCalibrationType(isAuto));
}
int FrogmoreScd30::setCalibrationType(bool isAuto)
{
bool value = !!isAuto; // using NOT operator twice makes sure value is 0 or 1
return (sendCommandArguments(COMMAND_SCD30_CALIBRATION_TYPE, value));
}
int FrogmoreScd30::setForcedRecalibrationFactor(uint16_t co2_ppm)
{
return (sendCommandArguments(COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR, co2_ppm));
}
int FrogmoreScd30::setManualCalibration(void)
{
bool isAuto = false;
return (setCalibrationType(isAuto));
}
int FrogmoreScd30::setMeasurementInterval(uint16_t time_sec)
{
if (time_sec < 2) time_sec = 2;
if (time_sec > 1800) time_sec = 1800;
return (sendCommandArguments(COMMAND_SCD30_MEASUREMENT_INTERVAL, time_sec));
}
int FrogmoreScd30::setTemperatureOffset(float offset_degC)
{
uint16_t offset_centiDegC;
if (offset_degC >= 0)
{
offset_centiDegC = (uint16_t) offset_degC * 100;
return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC));
}
else
{
return (ERROR_SCD30_INVALID_VALUE);
}
}
int FrogmoreScd30::setTemperatureOffset(uint16_t offset_centiDegC)
{
return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC));
}
int FrogmoreScd30::beginMeasuring(void)
{
return (beginMeasuring(ambientPressure));
}
int FrogmoreScd30::beginMeasuring(uint16_t airPressure_mbar)
{
ambientPressure = airPressure_mbar;
return(sendCommandArguments(COMMAND_SCD30_CONTINUOUS_MEASUREMENT, ambientPressure));
}
int FrogmoreScd30::isDataAvailable(bool *pIsAvailable)
{
uint16_t isDataAvailable = false;
int error = readRegister(COMMAND_SCD30_GET_DATA_READY, &isDataAvailable);
if (!error)
{
*pIsAvailable = isDataAvailable != 0;
}
return (error);
}
int FrogmoreScd30::readMeasurement(
uint16 *pCO2_ppm,
uint16 *pCO2EAvg_ppm,
float *pTemperature,
float *pHumidity
)
{
bool isAvailable = false;
int error = 0;
float tempCO2;
float tempHumidity;
float tempTemperature;
error = isDataAvailable(&isAvailable);
if (error)
{
return (error);
}
if (!isAvailable)
{
return (ERROR_SCD30_NO_DATA);
}
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: have data");
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
error = sendCommand(COMMAND_SCD30_READ_MEASUREMENT);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: send command failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
delay(1); // the SCD30 uses clock streching to give it time to prepare data, waiting here makes it work
uint8_t bytes[SCD30_MEAS_BYTES];
// there are (6) 16-bit values, each with a CRC in the measurement data
// the chip does not seem to like sending this data, except all at once
error = getBytes(bytes, SCD30_MEAS_BYTES);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes command failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]);
AddLog(LOG_LEVEL_DEBUG_MORE);
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], bytes[17]);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
error = get32BitRegCheckCRC(&bytes[0], &tempCO2);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 1st command failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
error = get32BitRegCheckCRC(&bytes[6], &tempTemperature);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 2nd command failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
error = get32BitRegCheckCRC(&bytes[12], &tempHumidity);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 3rd command failed: 0x%lX", error);
AddLog(LOG_LEVEL_INFO);
#endif
return (error);
}
if (tempCO2 == 0)
{
return (ERROR_SCD30_CO2_ZERO);
}
if (co2NewDataLocation < 0)
{
co2EAverage = tempCO2;
for (int x = 0; x < SCD30_MEDIAN_FILTER_SIZE; x++)
{
co2History[x] = tempCO2;
co2NewDataLocation = 1;
}
}
else
{
co2History[co2NewDataLocation++] = tempCO2;
if (co2NewDataLocation >= SCD30_MEDIAN_FILTER_SIZE)
{
co2NewDataLocation = 0;
}
}
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: co2History: %ld, %ld, %ld, %ld, %ld", co2History[0], co2History[1], co2History[2], co2History[3], co2History[4]);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
// copy array since the median filter function will re-arrange it
uint16_t temp[SCD30_MEDIAN_FILTER_SIZE];
for (int x = 0; x < SCD30_MEDIAN_FILTER_SIZE; x++)
{
temp[x] = co2History[x];
}
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: temp: %ld, %ld, %ld, %ld, %ld", temp[0], temp[1], temp[2], temp[3], temp[4]);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
*pCO2_ppm = opt_med5(temp);
#ifdef SCD30_DEBUG
snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: CO2_ppm: %ld", *pCO2_ppm);
AddLog(LOG_LEVEL_DEBUG_MORE);
#endif
if (pCO2EAvg_ppm)
{
int16_t delta = (int16_t) *pCO2_ppm - (int16_t) co2EAverage;
int16_t change = delta / 32;
co2EAverage += change;
#if 0
uint16_t remain = co2EAverage % 5;
uint16_t dividend = co2EAverage / 5;
uint16_t co2EAReported = dividend * 5;
if (remain > 2)
{
co2EAReported += 5;
}
*pCO2EAvg_ppm = co2EAReported;
#else
*pCO2EAvg_ppm = co2EAverage;
#endif
}
*pTemperature = tempTemperature;
*pHumidity = tempHumidity;
return (ERROR_SCD30_NO_ERROR);
}
int FrogmoreScd30::stopMeasuring(void)
{
return (sendCommand(COMMAND_SCD30_STOP_MEASUREMENT));
}

View File

@ -0,0 +1,105 @@
/*
# Copyright (c) 2019 Frogmore42
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include "Arduino.h"
//#define SCD30_DEBUG
#define SCD30_ADDRESS 0x61
#define ERROR_SCD30_NO_ERROR 0
#define ERROR_SCD30_NO_DATA 0x80000000
#define ERROR_SCD30_CO2_ZERO 0x90000000
#define ERROR_SCD30_UNKNOWN_ERROR 0x1000000
#define ERROR_SCD30_CRC_ERROR 0x2000000
#define ERROR_SCD30_NOT_ENOUGH_BYTES_ERROR 0x3000000
#define ERROR_SCD30_NOT_FOUND_ERROR 0x4000000
#define ERROR_SCD30_NOT_A_NUMBER_ERROR 0x5000000
#define ERROR_SCD30_INVALID_VALUE 0x6000000
#define SCD30_MEDIAN_FILTER_SIZE 5
class FrogmoreScd30
{
public:
FrogmoreScd30() {};
// Constructors
// the SCD30 only lists a single i2c address, so not necesary to specify
//
void begin(void);
void begin(uint8_t _i2cAddress);
void begin(TwoWire *pWire);
void begin(TwoWire *pWire, uint8_t _i2cAddress);
int softReset(void);
int clearI2CBus(void); // this is a HARD reset of the IC2 bus to restore communication, it will disrupt the bus
int getAltitudeCompensation(uint16_t *pHeight_meter);
int getAmbientPressure(uint16_t *pAirPressure_mbar);
int getCalibrationType(uint16_t *pIsAuto);
int getFirmwareVersion(uint8_t *pMajor, uint8_t *pMinor);
int getForcedRecalibrationFactor(uint16_t *pCo2_ppm);
int getMeasurementInterval(uint16_t *pTime_sec);
int getTemperatureOffset(float *pOffset_degC);
int getTemperatureOffset(uint16_t *pOffset_centiDegC);
int setAltitudeCompensation(uint16_t height_meter);
int setAmbientPressure(uint16_t airPressure_mbar);
int setAutoSelfCalibration(void);
int setCalibrationType(bool isAuto);
int setForcedRecalibrationFactor(uint16_t co2_ppm);
int setManualCalibration(void);
int setMeasurementInterval(uint16_t time_sec);
int setTemperatureOffset(float offset_degC);
int setTemperatureOffset(uint16_t offset_centiDegC);
int beginMeasuring(void);
int beginMeasuring(uint16_t airPressure_mbar); // also sets ambient pressure offset in mbar/hPascal
int isDataAvailable(bool *pIsAvailable);
int readMeasurement(
uint16 *pCO2_ppm,
uint16 *pCO2EAvg_ppm,
float *pTemperature,
float *pHumidity
);
int stopMeasuring(void);
private:
uint8_t i2cAddress;
TwoWire *pWire;
uint16_t ambientPressure;
uint16_t co2AvgExtra;
uint16_t co2History[SCD30_MEDIAN_FILTER_SIZE];
uint16_t co2EAverage;
int8_t co2NewDataLocation; // location to put new CO2 data for median filter
uint8_t computeCRC8(uint8_t data[], uint8_t len);
int sendBytes(void *pInput, uint8_t len);
int getBytes(void *pOutput, uint8_t len);
int sendCommand(uint16_t command);
int sendCommandArguments(uint16_t command, uint16_t arguments);
int get16BitRegCheckCRC(void* pInput, uint16_t* pData);
int get32BitRegCheckCRC(void* pInput, float* pData);
int readRegister(uint16_t registerAddress, uint16_t* pData);
#ifdef SCD30_DEBUG
void AddLog(uint8_t loglevel);
#endif
};

View File

@ -48,7 +48,11 @@ extern "C"
#include "ets_sys.h"
#include "user_interface.h"
// Workaround STAGE compile error
#include <core_version.h>
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0)
void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata);
#endif
}
struct slc_queue_item
@ -74,7 +78,7 @@ public:
class NeoEsp8266DmaSpeed800Kbps
{
public:
const static uint32_t I2sClockDivisor = 3;
const static uint32_t I2sClockDivisor = 3;
const static uint32_t I2sBaseClockDivisor = 16;
const static uint32_t ResetTimeUs = 50;
};
@ -82,7 +86,7 @@ public:
class NeoEsp8266DmaSpeed400Kbps
{
public:
const static uint32_t I2sClockDivisor = 6;
const static uint32_t I2sClockDivisor = 6;
const static uint32_t I2sBaseClockDivisor = 16;
const static uint32_t ResetTimeUs = 50;
};
@ -100,7 +104,7 @@ const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted t
template<typename T_SPEED> class NeoEsp8266DmaMethodBase
{
public:
NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize)
NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize)
{
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
@ -269,7 +273,7 @@ public:
private:
static NeoEsp8266DmaMethodBase* s_this; // for the ISR
size_t _pixelsSize; // Size of '_pixels' buffer
size_t _pixelsSize; // Size of '_pixels' buffer
uint8_t* _pixels; // Holds LED color values
uint32_t _i2sBufferSize; // total size of _i2sBuffer
@ -310,7 +314,7 @@ private:
slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA;
// data block has pending data waiting to send, prepare it
// point last state block to top
// point last state block to top
(finished_item + 1)->next_link_ptr = (uint32_t)(s_this->_i2sBufDesc);
s_this->_dmaState = NeoDmaState_Sending;
@ -367,7 +371,7 @@ private:
}
};
template<typename T_SPEED>
template<typename T_SPEED>
NeoEsp8266DmaMethodBase<T_SPEED>* NeoEsp8266DmaMethodBase<T_SPEED>::s_this;
typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaSpeedWs2813> NeoEsp8266DmaWs2813Method;

View File

@ -1,28 +0,0 @@
# C++ objects and libs
*.slo
*.lo
*.o
#*.a
*.la
*.lai
*.so
*.dll
*.dylib
#Makefile
*-build-*
build-*
*.autosave
# .log files (usually created by QtTest - thanks to VestniK)
*.log
# Editors temporary files
*~
#OSX
.DS_Store
._*

View File

@ -1,8 +0,0 @@
MQTT
====
A Wrapper around mqtt for Arduino to be used with esp8266 modules.
It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM.
Original code for esp: https://github.com/tuanpmt/esp_mqtt
Original code for contiki: https://github.com/esar/contiki-mqtt

View File

@ -1,102 +0,0 @@
#include <ESP8266WiFi.h>
#include <MQTT.h>
void myDataCb(String& topic, String& data);
void myPublishedCb();
void myDisconnectedCb();
void myConnectedCb();
#define CLIENT_ID "client1"
// create MQTT object
MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883);
//
const char* ssid = "ssid";
const char* password = "ssid_password";
//
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Connecting to MQTT server");
// setup callbacks
myMqtt.onConnected(myConnectedCb);
myMqtt.onDisconnected(myDisconnectedCb);
myMqtt.onPublished(myPublishedCb);
myMqtt.onData(myDataCb);
Serial.println("connect mqtt...");
myMqtt.connect();
delay(10);
}
//
void loop() {
int value = analogRead(A0);
String topic("/");
topic += CLIENT_ID;
topic += "/value";
String valueStr(value);
// publish value to topic
boolean result = myMqtt.publish(topic, valueStr);
delay(1000);
}
/*
*
*/
void myConnectedCb()
{
Serial.println("connected to MQTT server");
}
void myDisconnectedCb()
{
Serial.println("disconnected. try to reconnect...");
delay(500);
myMqtt.connect();
}
void myPublishedCb()
{
//Serial.println("published.");
}
void myDataCb(String& topic, String& data)
{
Serial.print(topic);
Serial.print(": ");
Serial.println(data);
}

View File

@ -1,97 +0,0 @@
#include <ESP8266WiFi.h>
#include <TasmotaMqtt.h>
// This needs testing
void myDataCb(char* topic, uint8_t* data, unsigned int data_len);
void myPublishedCb();
void myDisconnectedCb();
void myConnectedCb();
#define CLIENT_ID "client3"
#define TOPIC "/client1/value"
// create MQTT
TasmotaMqtt myMqtt();
const char* ssid = "ssid";
const char* password = "ssid_password";
//
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Connecting to MQTT server");
myMqtt.InitConnection("192.168.0.1", 1883);
myMqtt.InitClient(CLIENT_ID, "", "");
myMqtt.InitLWT("/lwt", "offline");
// setup callbacks
myMqtt.OnConnected(myConnectedCb);
myMqtt.OnDisconnected(myDisconnectedCb);
myMqtt.OnPublished(myPublishedCb);
myMqtt.OnData(myDataCb);
Serial.println("connect mqtt...");
myMqtt.Connect();
Serial.println("subscribe to topic...");
myMqtt.Subscribe(TOPIC);
delay(10);
}
//
void loop() {
}
/*
*
*/
void myConnectedCb()
{
Serial.println("connected to MQTT server");
}
void myDisconnectedCb()
{
Serial.println("disconnected. try to reconnect...");
delay(500);
myMqtt.Connect();
}
void myPublishedCb()
{
//Serial.println("published.");
}
void myDataCb(char* topic, uint8_t* data, unsigned int data_len)
{
Serial.print(topic);
Serial.print(": ");
Serial.println(data);
}

View File

@ -1,39 +0,0 @@
#######################################
# Syntax Coloring Map For Test
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
TasmotaMqtt.h KEYWORD1
TasmotaMqtt KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
InitConnection KEYWORD2
InitClient KEYWORD2
InitLWT KEYWORD2
Connect KEYWORD2
Disconnect KEYWORD2
Connected KEYWORD2
Publish KEYWORD2
Subscribe KEYWORD2
#general
OnConnected KEYWORD2
OnDisconnected KEYWORD2
OnData KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -1,9 +0,0 @@
name=TasmotaMqtt
version=1.0.0
author=Theo Arends
maintainer=Theo Arends <theo@arends.com>
sentence=A Wrapper around mqtt for Arduino to be used with esp8266 modules.
paragraph=It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. Original code for esp: https://github.com/tuanpmt/esp_mqtt Original code for contiki: https://github.com/esar/contiki-mqtt
category=Communication
url=
architectures=esp8266

View File

@ -1,194 +0,0 @@
/*
TasmotaMqtt.cpp - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota
Copyright (C) 2018 Theo Arends and Ingo Randolf
This library 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/>.
*/
#include "TasmotaMqtt.h"
#include "user_interface.h"
#include "osapi.h"
#include "os_type.h"
/*********************************************************************************************\
* Prerequisite
*
* Copy .c and .h files from https://github.com/tuanpmt/esp_mqtt folder mqtt to folder mqtt
* - Replace BOOL with bool
* - Remove variables certificate and private_key from file mqtt.c
* - Add file user_config.h with default defines for
* MQTT_BUF_SIZE 256, MQTT_RECONNECT_TIMEOUT 5, QUEUE_BUFFER_SIZE 2048 and PROTOCOL_NAMEv311
\*********************************************************************************************/
/*********************************************************************************************\
* Mqtt internal callbacks
\*********************************************************************************************/
static void mqttConnectedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data;
if (_this && _this->onMqttConnectedCb) _this->onMqttConnectedCb();
}
static void mqttDisconnectedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data;
if (_this && _this->onMqttDisconnectedCb) _this->onMqttDisconnectedCb();
}
static void mqttPublishedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data;
if (_this && _this->onMqttPublishedCb) _this->onMqttPublishedCb();
}
static void mqttTimeoutCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data;
if (_this && _this->onMqttTimeoutCb) _this->onMqttTimeoutCb();
}
static void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len)
{
MQTT_Client* client = (MQTT_Client*)args;
TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data;
if (_this) _this->_onMqttDataCb(topic, topic_len, data, data_len);
}
/*********************************************************************************************\
* TasmotaMqtt class implementation
\*********************************************************************************************/
TasmotaMqtt::TasmotaMqtt() :
onMqttConnectedCb(0),
onMqttDisconnectedCb(0),
onMqttPublishedCb(0),
onMqttTimeoutCb(0),
onMqttDataCb(0)
{
}
TasmotaMqtt::~TasmotaMqtt()
{
MQTT_DeleteClient(&mqttClient);
}
void TasmotaMqtt::InitConnection(const char* host, uint32_t port, uint8_t security)
{
MQTT_InitConnection(&mqttClient, (uint8_t*)host, port, security);
// set user data
mqttClient.user_data = (void*)this;
MQTT_OnConnected(&mqttClient, mqttConnectedCb);
MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb);
MQTT_OnPublished(&mqttClient, mqttPublishedCb);
MQTT_OnTimeout(&mqttClient, mqttTimeoutCb);
MQTT_OnData(&mqttClient, mqttDataCb);
}
void TasmotaMqtt::InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time, uint8_t clean_session)
{
MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, keep_alive_time, clean_session);
}
void TasmotaMqtt::DeleteClient()
{
MQTT_DeleteClient(&mqttClient);
}
void TasmotaMqtt::InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain)
{
MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain);
}
void TasmotaMqtt::OnConnected( void (*function)(void) )
{
onMqttConnectedCb = function;
}
void TasmotaMqtt::OnDisconnected( void (*function)(void) )
{
onMqttDisconnectedCb = function;
}
void TasmotaMqtt::OnPublished( void (*function)(void) )
{
onMqttPublishedCb = function;
}
void TasmotaMqtt::OnTimeout( void (*function)(void) )
{
onMqttTimeoutCb = function;
}
void TasmotaMqtt::OnData( void (*function)(char*, uint8_t*, unsigned int) )
{
onMqttDataCb = function;
}
bool TasmotaMqtt::Subscribe(const char* topic, uint8_t qos)
{
return MQTT_Subscribe(&mqttClient, (char*)topic, qos);
}
bool TasmotaMqtt::Unsubscribe(const char* topic)
{
return MQTT_UnSubscribe(&mqttClient, (char*)topic);
}
void TasmotaMqtt::Connect()
{
MQTT_Connect(&mqttClient);
}
void TasmotaMqtt::Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain)
{
MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, MQTT_KEEPALIVE, 1);
MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain);
MQTT_Connect(&mqttClient);
}
void TasmotaMqtt::Disconnect()
{
MQTT_Disconnect(&mqttClient);
}
bool TasmotaMqtt::Publish(const char* topic, const char* data, int data_length, int qos, bool retain)
{
return MQTT_Publish(&mqttClient, topic, data, data_length, qos, (int)retain);
}
bool TasmotaMqtt::Connected()
{
return (mqttClient.connState > TCP_CONNECTED);
}
/*********************************************************************************************/
void TasmotaMqtt::_onMqttDataCb(const char* topic, uint32_t topic_len, const char* data, uint32_t data_len)
{
char topic_copy[topic_len +1];
memcpy(topic_copy, topic, topic_len);
topic_copy[topic_len] = 0;
if (0 == data_len) data = (const char*)&topic_copy + topic_len;
onMqttDataCb((char*)topic_copy, (byte*)data, data_len);
}

View File

@ -1,88 +0,0 @@
/*
TasmotaMqtt.h - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota
Copyright (C) 2018 Theo Arends and Ingo Randolf
This library 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/>.
*/
#ifndef TasmotaMqtt_h
#define TasmotaMqtt_h
/*********************************************************************************************\
* TasmotaMqtt supports currently only non-TLS MQTT
*
* Adapted from esp-mqtt-arduino by Ingo Randolf (https://github.com/i-n-g-o/esp-mqtt-arduino)
\*********************************************************************************************/
#include <Arduino.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
extern "C" {
#include <stddef.h>
#include "mqtt/mqtt.h"
}
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 15
#endif
class TasmotaMqtt {
public:
TasmotaMqtt();
~TasmotaMqtt();
void InitConnection(const char* host, uint32_t port, uint8_t security = 0);
void InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time = MQTT_KEEPALIVE, uint8_t clean_session = 1);
void DeleteClient();
void InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false);
void OnConnected( void (*)(void) );
void OnDisconnected( void (*)(void) );
void OnPublished( void (*)(void) );
void OnTimeout( void (*)(void) );
void OnData( void (*)(char*, uint8_t*, unsigned int) );
bool Subscribe(const char* topic, uint8_t qos = 0);
bool Unsubscribe(const char* topic);
void Connect();
void Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false);
void Disconnect();
bool Publish(const char* topic, const char* data, int data_length, int qos = 0, bool retain = false);
bool Connected();
int State() { return mqttClient.connState; };
void (*onMqttConnectedCb)(void);
void (*onMqttDisconnectedCb)(void);
void (*onMqttPublishedCb)(void);
void (*onMqttTimeoutCb)(void);
void (*onMqttDataCb) (char*, uint8_t*, unsigned int);
// internal callback
void _onMqttDataCb(const char*, uint32_t, const char*, uint32_t);
private:
MQTT_Client mqttClient;
};
#endif // TasmotaMqtt_h

View File

@ -1,19 +0,0 @@
/*
* debug.h
*
* Created on: Dec 4, 2014
* Author: Minh
*/
#ifndef USER_DEBUG_H_
#define USER_DEBUG_H_
#if defined(MQTT_DEBUG_ON)
#define MQTT_INFO( format, ... ) os_printf( format, ## __VA_ARGS__ )
#else
#define MQTT_INFO( format, ... )
#endif
#endif /* USER_DEBUG_H_ */

View File

@ -1,997 +0,0 @@
/* mqtt.c
* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "user_interface.h"
#include "osapi.h"
#include "espconn.h"
#include "os_type.h"
#include "mem.h"
#include "mqtt_msg.h"
#include "debug.h"
#include "user_config.h"
#include "mqtt.h"
#include "queue.h"
#define MQTT_TASK_PRIO 2
#define MQTT_TASK_QUEUE_SIZE 1
#define MQTT_SEND_TIMOUT 5
#ifndef MQTT_SSL_SIZE
#define MQTT_SSL_SIZE 5120
#endif
#ifndef QUEUE_BUFFER_SIZE
#define QUEUE_BUFFER_SIZE 2048
#endif
/*
unsigned char *default_certificate;
unsigned int default_certificate_len = 0;
unsigned char *default_private_key;
unsigned int default_private_key_len = 0;
*/
os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE];
#ifdef PROTOCOL_NAMEv311
LOCAL uint8_t zero_len_id[2] = { 0, 0 };
#endif
LOCAL void ICACHE_FLASH_ATTR
mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
{
struct espconn *pConn = (struct espconn *)arg;
MQTT_Client* client = (MQTT_Client *)pConn->reverse;
if (ipaddr == NULL)
{
MQTT_INFO("DNS: Found, but got no ip, try to reconnect\r\n");
client->connState = TCP_RECONNECT_REQ;
return;
}
MQTT_INFO("DNS: found ip %d.%d.%d.%d\n",
*((uint8 *) &ipaddr->addr),
*((uint8 *) &ipaddr->addr + 1),
*((uint8 *) &ipaddr->addr + 2),
*((uint8 *) &ipaddr->addr + 3));
if (client->ip.addr == 0 && ipaddr->addr != 0)
{
os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4);
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE);
espconn_secure_connect(client->pCon);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_connect(client->pCon);
}
client->connState = TCP_CONNECTING;
MQTT_INFO("TCP: connecting...\r\n");
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
LOCAL void ICACHE_FLASH_ATTR
deliver_publish(MQTT_Client* client, uint8_t* message, int length)
{
mqtt_event_data_t event_data;
event_data.topic_length = length;
event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length);
event_data.data_length = length;
event_data.data = mqtt_get_publish_data(message, &event_data.data_length);
if (client->dataCb)
client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length);
}
void ICACHE_FLASH_ATTR
mqtt_send_keepalive(MQTT_Client *client)
{
MQTT_INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port);
client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection);
client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ;
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
client->sendTimeout = MQTT_SEND_TIMOUT;
MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
err_t result = ESPCONN_OK;
if (client->security) {
#ifdef MQTT_SSL_ENABLE
result = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
}
client->mqtt_state.outbound_message = NULL;
if (ESPCONN_OK == result) {
client->keepAliveTick = 0;
client->connState = MQTT_DATA;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
else {
client->connState = TCP_RECONNECT_DISCONNECTING;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
}
/**
* @brief Delete tcp client and free all memory
* @param mqttClient: The mqtt client which contain TCP client
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_tcpclient_delete(MQTT_Client *mqttClient)
{
if (mqttClient->pCon != NULL) {
MQTT_INFO("TCP: Free memory\r\n");
// Force abort connections
espconn_abort(mqttClient->pCon);
// Delete connections
espconn_delete(mqttClient->pCon);
if (mqttClient->pCon->proto.tcp) {
os_free(mqttClient->pCon->proto.tcp);
mqttClient->pCon->proto.tcp = NULL;
}
os_free(mqttClient->pCon);
mqttClient->pCon = NULL;
}
}
/**
* @brief Delete MQTT client and free all memory
* @param mqttClient: The mqtt client
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_client_delete(MQTT_Client *mqttClient)
{
if (mqttClient == NULL)
return;
if (mqttClient->pCon != NULL) {
mqtt_tcpclient_delete(mqttClient);
}
if (mqttClient->host != NULL) {
os_free(mqttClient->host);
mqttClient->host = NULL;
}
if (mqttClient->user_data != NULL) {
os_free(mqttClient->user_data);
mqttClient->user_data = NULL;
}
if (mqttClient->mqtt_state.in_buffer != NULL) {
os_free(mqttClient->mqtt_state.in_buffer);
mqttClient->mqtt_state.in_buffer = NULL;
}
if (mqttClient->mqtt_state.out_buffer != NULL) {
os_free(mqttClient->mqtt_state.out_buffer);
mqttClient->mqtt_state.out_buffer = NULL;
}
if (mqttClient->mqtt_state.outbound_message != NULL) {
if (mqttClient->mqtt_state.outbound_message->data != NULL)
{
os_free(mqttClient->mqtt_state.outbound_message->data);
mqttClient->mqtt_state.outbound_message->data = NULL;
}
}
if (mqttClient->mqtt_state.mqtt_connection.buffer != NULL) {
// Already freed but not NULL
mqttClient->mqtt_state.mqtt_connection.buffer = NULL;
}
if (mqttClient->connect_info.client_id != NULL) {
#ifdef PROTOCOL_NAMEv311
/* Don't attempt to free if it's the zero_len array */
if ( ((uint8_t*)mqttClient->connect_info.client_id) != zero_len_id )
os_free(mqttClient->connect_info.client_id);
#else
os_free(mqttClient->connect_info.client_id);
#endif
mqttClient->connect_info.client_id = NULL;
}
if (mqttClient->connect_info.username != NULL) {
os_free(mqttClient->connect_info.username);
mqttClient->connect_info.username = NULL;
}
if (mqttClient->connect_info.password != NULL) {
os_free(mqttClient->connect_info.password);
mqttClient->connect_info.password = NULL;
}
if (mqttClient->connect_info.will_topic != NULL) {
os_free(mqttClient->connect_info.will_topic);
mqttClient->connect_info.will_topic = NULL;
}
if (mqttClient->connect_info.will_message != NULL) {
os_free(mqttClient->connect_info.will_message);
mqttClient->connect_info.will_message = NULL;
}
if (mqttClient->msgQueue.buf != NULL) {
os_free(mqttClient->msgQueue.buf);
mqttClient->msgQueue.buf = NULL;
}
// Initialize state
mqttClient->connState = WIFI_INIT;
// Clear callback functions to avoid abnormal callback
mqttClient->connectedCb = NULL;
mqttClient->disconnectedCb = NULL;
mqttClient->publishedCb = NULL;
mqttClient->timeoutCb = NULL;
mqttClient->dataCb = NULL;
MQTT_INFO("MQTT: client already deleted\r\n");
}
/**
* @brief Client received callback function.
* @param arg: contain the ip link information
* @param pdata: received data
* @param len: the lenght of received data
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len)
{
uint8_t msg_type;
uint8_t msg_qos;
uint16_t msg_id;
uint8_t msg_conn_ret;
struct espconn *pCon = (struct espconn*)arg;
MQTT_Client *client = (MQTT_Client *)pCon->reverse;
READPACKET:
MQTT_INFO("TCP: data received %d bytes\r\n", len);
// MQTT_INFO("STATE: %d\r\n", client->connState);
if (len < MQTT_BUF_SIZE && len > 0) {
os_memcpy(client->mqtt_state.in_buffer, pdata, len);
msg_type = mqtt_get_type(client->mqtt_state.in_buffer);
msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer);
msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);
switch (client->connState) {
case MQTT_CONNECT_SENDING:
if (msg_type == MQTT_MSG_TYPE_CONNACK) {
if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) {
MQTT_INFO("MQTT: Invalid packet\r\n");
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_disconnect(client->pCon);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_disconnect(client->pCon);
}
} else {
msg_conn_ret = mqtt_get_connect_return_code(client->mqtt_state.in_buffer);
switch (msg_conn_ret) {
case CONNECTION_ACCEPTED:
MQTT_INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port);
client->connState = MQTT_DATA;
if (client->connectedCb)
client->connectedCb((uint32_t*)client);
break;
case CONNECTION_REFUSE_PROTOCOL:
case CONNECTION_REFUSE_SERVER_UNAVAILABLE:
case CONNECTION_REFUSE_BAD_USERNAME:
case CONNECTION_REFUSE_NOT_AUTHORIZED:
MQTT_INFO("MQTT: Connection refuse, reason code: %d\r\n", msg_conn_ret);
default:
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_disconnect(client->pCon);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_disconnect(client->pCon);
}
}
}
}
break;
case MQTT_DATA:
case MQTT_KEEPALIVE_SEND:
client->mqtt_state.message_length_read = len;
client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);
switch (msg_type)
{
case MQTT_MSG_TYPE_SUBACK:
if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id)
MQTT_INFO("MQTT: Subscribe successful\r\n");
break;
case MQTT_MSG_TYPE_UNSUBACK:
if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id)
MQTT_INFO("MQTT: UnSubscribe successful\r\n");
break;
case MQTT_MSG_TYPE_PUBLISH:
if (msg_qos == 1)
client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);
else if (msg_qos == 2)
client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);
if (msg_qos == 1 || msg_qos == 2) {
MQTT_INFO("MQTT: Queue response QoS: %d\r\n", msg_qos);
if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
}
}
deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);
break;
case MQTT_MSG_TYPE_PUBACK:
if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) {
MQTT_INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n");
}
break;
case MQTT_MSG_TYPE_PUBREC:
client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);
if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
}
break;
case MQTT_MSG_TYPE_PUBREL:
client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);
if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
}
break;
case MQTT_MSG_TYPE_PUBCOMP:
if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) {
MQTT_INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n");
}
break;
case MQTT_MSG_TYPE_PINGREQ:
client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection);
if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
}
break;
case MQTT_MSG_TYPE_PINGRESP:
// Ignore
break;
}
// NOTE: this is done down here and not in the switch case above
// because the PSOCK_READBUF_LEN() won't work inside a switch
// statement due to the way protothreads resume.
if (msg_type == MQTT_MSG_TYPE_PUBLISH)
{
len = client->mqtt_state.message_length_read;
if (client->mqtt_state.message_length < client->mqtt_state.message_length_read)
{
//client->connState = MQTT_PUBLISH_RECV;
//Not Implement yet
len -= client->mqtt_state.message_length;
pdata += client->mqtt_state.message_length;
MQTT_INFO("Get another published message\r\n");
goto READPACKET;
}
}
break;
}
} else {
MQTT_INFO("ERROR: Message too long\r\n");
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
/**
* @brief Client send over callback function.
* @param arg: contain the ip link information
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_tcpclient_sent_cb(void *arg)
{
struct espconn *pCon = (struct espconn *)arg;
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
MQTT_INFO("TCP: Sent\r\n");
client->sendTimeout = 0;
client->keepAliveTick = 0;
if ((client->connState == MQTT_DATA || client->connState == MQTT_KEEPALIVE_SEND)
&& client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) {
if (client->publishedCb)
client->publishedCb((uint32_t*)client);
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
void ICACHE_FLASH_ATTR mqtt_timer(void *arg)
{
MQTT_Client* client = (MQTT_Client*)arg;
if (client->connState == MQTT_DATA) {
client->keepAliveTick ++;
if (client->keepAliveTick > (client->mqtt_state.connect_info->keepalive / 2)) {
client->connState = MQTT_KEEPALIVE_SEND;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
} else if (client->connState == TCP_RECONNECT_REQ) {
client->reconnectTick ++;
if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) {
client->reconnectTick = 0;
client->connState = TCP_RECONNECT;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
if (client->timeoutCb)
client->timeoutCb((uint32_t*)client);
}
}
if (client->sendTimeout > 0)
client->sendTimeout --;
}
void ICACHE_FLASH_ATTR
mqtt_tcpclient_discon_cb(void *arg)
{
struct espconn *pespconn = (struct espconn *)arg;
MQTT_Client* client = (MQTT_Client *)pespconn->reverse;
MQTT_INFO("TCP: Disconnected callback\r\n");
if (TCP_DISCONNECTING == client->connState) {
client->connState = TCP_DISCONNECTED;
}
else if (MQTT_DELETING == client->connState) {
client->connState = MQTT_DELETED;
}
else {
client->connState = TCP_RECONNECT_REQ;
}
if (client->disconnectedCb)
client->disconnectedCb((uint32_t*)client);
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
/**
* @brief Tcp client connect success callback function.
* @param arg: contain the ip link information
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_tcpclient_connect_cb(void *arg)
{
struct espconn *pCon = (struct espconn *)arg;
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb);
espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);////////
espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);///////
MQTT_INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port);
mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length);
client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info);
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
client->sendTimeout = MQTT_SEND_TIMOUT;
MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);
}
client->mqtt_state.outbound_message = NULL;
client->connState = MQTT_CONNECT_SENDING;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
/**
* @brief Tcp client connect repeat callback function.
* @param arg: contain the ip link information
* @retval None
*/
void ICACHE_FLASH_ATTR
mqtt_tcpclient_recon_cb(void *arg, sint8 errType)
{
struct espconn *pCon = (struct espconn *)arg;
MQTT_Client* client = (MQTT_Client *)pCon->reverse;
MQTT_INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port);
client->connState = TCP_RECONNECT_REQ;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
}
/**
* @brief MQTT publish function.
* @param client: MQTT_Client reference
* @param topic: string topic will publish to
* @param data: buffer data send point to
* @param data_length: length of data
* @param qos: qos
* @param retain: retain
* @retval TRUE if success queue
*/
bool ICACHE_FLASH_ATTR
MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain)
{
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,
topic, data, data_length,
qos, retain,
&client->mqtt_state.pending_msg_id);
if (client->mqtt_state.outbound_message->length == 0) {
MQTT_INFO("MQTT: Queuing publish failed\r\n");
return FALSE;
}
MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);
while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
MQTT_INFO("MQTT: Serious buffer error\r\n");
return FALSE;
}
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
return TRUE;
}
/**
* @brief MQTT subscibe function.
* @param client: MQTT_Client reference
* @param topic: string topic will subscribe
* @param qos: qos
* @retval TRUE if success queue
*/
bool ICACHE_FLASH_ATTR
MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos)
{
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection,
topic, qos,
&client->mqtt_state.pending_msg_id);
MQTT_INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id);
while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
MQTT_INFO("MQTT: Serious buffer error\r\n");
return FALSE;
}
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
return TRUE;
}
/**
* @brief MQTT un-subscibe function.
* @param client: MQTT_Client reference
* @param topic: String topic will un-subscribe
* @retval TRUE if success queue
*/
bool ICACHE_FLASH_ATTR
MQTT_UnSubscribe(MQTT_Client *client, char* topic)
{
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection,
topic,
&client->mqtt_state.pending_msg_id);
MQTT_INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id);
while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
MQTT_INFO("MQTT: Serious buffer error\r\n");
return FALSE;
}
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
return TRUE;
}
/**
* @brief MQTT ping function.
* @param client: MQTT_Client reference
* @retval TRUE if success queue
*/
bool ICACHE_FLASH_ATTR
MQTT_Ping(MQTT_Client *client)
{
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection);
if (client->mqtt_state.outbound_message->length == 0) {
MQTT_INFO("MQTT: Queuing publish failed\r\n");
return FALSE;
}
MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);
while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {
MQTT_INFO("MQTT: Queue full\r\n");
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) {
MQTT_INFO("MQTT: Serious buffer error\r\n");
return FALSE;
}
}
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);
return TRUE;
}
void ICACHE_FLASH_ATTR
MQTT_Task(os_event_t *e)
{
MQTT_Client* client = (MQTT_Client*)e->par;
uint8_t dataBuffer[MQTT_BUF_SIZE];
uint16_t dataLen;
if (e->par == 0)
return;
switch (client->connState) {
case TCP_RECONNECT_REQ:
break;
case TCP_RECONNECT:
mqtt_tcpclient_delete(client);
MQTT_Connect(client);
MQTT_INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port);
client->connState = TCP_CONNECTING;
break;
case MQTT_DELETING:
case TCP_DISCONNECTING:
case TCP_RECONNECT_DISCONNECTING:
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_disconnect(client->pCon);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_disconnect(client->pCon);
}
break;
case TCP_DISCONNECTED:
MQTT_INFO("MQTT: Disconnected\r\n");
mqtt_tcpclient_delete(client);
break;
case MQTT_DELETED:
MQTT_INFO("MQTT: Deleted client\r\n");
mqtt_client_delete(client);
break;
case MQTT_KEEPALIVE_SEND:
mqtt_send_keepalive(client);
break;
case MQTT_DATA:
if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) {
break;
}
if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) {
client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer);
client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen);
client->sendTimeout = MQTT_SEND_TIMOUT;
MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);
client->keepAliveTick = 0;
if (client->security) {
#ifdef MQTT_SSL_ENABLE
espconn_secure_send(client->pCon, dataBuffer, dataLen);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else {
espconn_send(client->pCon, dataBuffer, dataLen);
}
client->mqtt_state.outbound_message = NULL;
break;
}
break;
}
}
/**
* @brief MQTT initialization connection function
* @param client: MQTT_Client reference
* @param host: Domain or IP string
* @param port: Port to connect
* @param security: 1 for ssl, 0 for none
* @retval None
*/
void ICACHE_FLASH_ATTR
MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security)
{
uint32_t temp;
MQTT_INFO("MQTT:InitConnection\r\n");
os_memset(mqttClient, 0, sizeof(MQTT_Client));
temp = os_strlen(host);
mqttClient->host = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->host, host);
mqttClient->host[temp] = 0;
mqttClient->port = port;
mqttClient->security = security;
}
/**
* @brief MQTT initialization mqtt client function
* @param client: MQTT_Client reference
* @param clientid: MQTT client id
* @param client_user:MQTT client user
* @param client_pass:MQTT client password
* @param client_pass:MQTT keep alive timer, in second
* @retval None
*/
bool ICACHE_FLASH_ATTR
MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession)
{
uint32_t temp;
MQTT_INFO("MQTT:InitClient\r\n");
os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t));
if ( !client_id )
{
/* Should be allowed by broker, but clean session flag must be set. */
#ifdef PROTOCOL_NAMEv311
if (cleanSession)
{
mqttClient->connect_info.client_id = zero_len_id;
} else {
MQTT_INFO("cleanSession must be set to use 0 length client_id\r\n");
return false;
}
/* Not supported. Return. */
#else
MQTT_INFO("Client ID required for MQTT < 3.1.1!\r\n");
return false;
#endif
}
/* If connect_info's client_id is still NULL and we get here, we can *
* assume the passed client_id is non-NULL. */
if ( !(mqttClient->connect_info.client_id) )
{
temp = os_strlen(client_id);
mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.client_id, client_id);
mqttClient->connect_info.client_id[temp] = 0;
}
if (client_user)
{
temp = os_strlen(client_user);
mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.username, client_user);
mqttClient->connect_info.username[temp] = 0;
}
if (client_pass)
{
temp = os_strlen(client_pass);
mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.password, client_pass);
mqttClient->connect_info.password[temp] = 0;
}
mqttClient->connect_info.keepalive = keepAliveTime;
mqttClient->connect_info.clean_session = cleanSession;
mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE);
mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE;
mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE);
mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE;
mqttClient->mqtt_state.connect_info = &mqttClient->connect_info;
mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length);
QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE);
system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);
return true;
}
void ICACHE_FLASH_ATTR
MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain)
{
uint32_t temp;
temp = os_strlen(will_topic);
mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.will_topic, will_topic);
mqttClient->connect_info.will_topic[temp] = 0;
temp = os_strlen(will_msg);
mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.will_message, will_msg);
mqttClient->connect_info.will_message[temp] = 0;
mqttClient->connect_info.will_qos = will_qos;
mqttClient->connect_info.will_retain = will_retain;
}
/**
* @brief Begin connect to MQTT broker
* @param client: MQTT_Client reference
* @retval None
*/
void ICACHE_FLASH_ATTR
MQTT_Connect(MQTT_Client *mqttClient)
{
if (mqttClient->pCon) {
// Clean up the old connection forcefully - using MQTT_Disconnect
// does not actually release the old connection until the
// disconnection callback is invoked.
mqtt_tcpclient_delete(mqttClient);
}
mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn));
mqttClient->pCon->type = ESPCONN_TCP;
mqttClient->pCon->state = ESPCONN_NONE;
mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
mqttClient->pCon->proto.tcp->local_port = espconn_port();
mqttClient->pCon->proto.tcp->remote_port = mqttClient->port;
mqttClient->pCon->reverse = mqttClient;
espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb);
espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb);
mqttClient->keepAliveTick = 0;
mqttClient->reconnectTick = 0;
os_timer_disarm(&mqttClient->mqttTimer);
os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient);
os_timer_arm(&mqttClient->mqttTimer, 1000, 1);
if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) {
MQTT_INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port);
if (mqttClient->security)
{
#ifdef MQTT_SSL_ENABLE
espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE);
espconn_secure_connect(mqttClient->pCon);
#else
MQTT_INFO("TCP: Do not support SSL\r\n");
#endif
}
else
{
espconn_connect(mqttClient->pCon);
}
}
else {
MQTT_INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port);
espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found);
}
mqttClient->connState = TCP_CONNECTING;
}
void ICACHE_FLASH_ATTR
MQTT_Disconnect(MQTT_Client *mqttClient)
{
mqttClient->connState = TCP_DISCONNECTING;
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);
os_timer_disarm(&mqttClient->mqttTimer);
}
void ICACHE_FLASH_ATTR
MQTT_DeleteClient(MQTT_Client *mqttClient)
{
if (NULL == mqttClient)
return;
mqttClient->connState = MQTT_DELETED;
// if(TCP_DISCONNECTED == mqttClient->connState) {
// mqttClient->connState = MQTT_DELETED;
// } else if(MQTT_DELETED != mqttClient->connState) {
// mqttClient->connState = MQTT_DELETING;
// }
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);
os_timer_disarm(&mqttClient->mqttTimer);
}
void ICACHE_FLASH_ATTR
MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb)
{
mqttClient->connectedCb = connectedCb;
}
void ICACHE_FLASH_ATTR
MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb)
{
mqttClient->disconnectedCb = disconnectedCb;
}
void ICACHE_FLASH_ATTR
MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb)
{
mqttClient->dataCb = dataCb;
}
void ICACHE_FLASH_ATTR
MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb)
{
mqttClient->publishedCb = publishedCb;
}
void ICACHE_FLASH_ATTR
MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb)
{
mqttClient->timeoutCb = timeoutCb;
}

View File

@ -1,148 +0,0 @@
/* mqtt.h
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef USER_AT_MQTT_H_
#define USER_AT_MQTT_H_
#include "user_config.h"
#include "mqtt_msg.h"
#include "user_interface.h"
#include "queue.h"
typedef struct mqtt_event_data_t
{
uint8_t type;
const char* topic;
const char* data;
uint16_t topic_length;
uint16_t data_length;
uint16_t data_offset;
} mqtt_event_data_t;
typedef struct mqtt_state_t
{
uint16_t port;
int auto_reconnect;
mqtt_connect_info_t* connect_info;
uint8_t* in_buffer;
uint8_t* out_buffer;
int in_buffer_length;
int out_buffer_length;
uint16_t message_length;
uint16_t message_length_read;
mqtt_message_t* outbound_message;
mqtt_connection_t mqtt_connection;
uint16_t pending_msg_id;
int pending_msg_type;
int pending_publish_qos;
} mqtt_state_t;
typedef enum {
WIFI_INIT,
WIFI_CONNECTING,
WIFI_CONNECTING_ERROR,
WIFI_CONNECTED,
DNS_RESOLVE,
TCP_DISCONNECTING,
TCP_DISCONNECTED,
TCP_RECONNECT_DISCONNECTING,
TCP_RECONNECT_REQ,
TCP_RECONNECT,
TCP_CONNECTING,
TCP_CONNECTING_ERROR,
TCP_CONNECTED,
MQTT_CONNECT_SEND,
MQTT_CONNECT_SENDING,
MQTT_SUBSCIBE_SEND,
MQTT_SUBSCIBE_SENDING,
MQTT_DATA,
MQTT_KEEPALIVE_SEND,
MQTT_PUBLISH_RECV,
MQTT_PUBLISHING,
MQTT_DELETING,
MQTT_DELETED,
} tConnState;
typedef void (*MqttCallback)(uint32_t *args);
typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh);
typedef struct {
struct espconn *pCon;
uint8_t security;
uint8_t* host;
uint32_t port;
ip_addr_t ip;
mqtt_state_t mqtt_state;
mqtt_connect_info_t connect_info;
MqttCallback connectedCb;
MqttCallback disconnectedCb;
MqttCallback publishedCb;
MqttCallback timeoutCb;
MqttDataCallback dataCb;
ETSTimer mqttTimer;
uint32_t keepAliveTick;
uint32_t reconnectTick;
uint32_t sendTimeout;
tConnState connState;
QUEUE msgQueue;
void* user_data;
} MQTT_Client;
#define SEC_NONSSL 0
#define SEC_SSL 1
#define MQTT_FLAG_CONNECTED 1
#define MQTT_FLAG_READY 2
#define MQTT_FLAG_EXIT 4
#define MQTT_EVENT_TYPE_NONE 0
#define MQTT_EVENT_TYPE_CONNECTED 1
#define MQTT_EVENT_TYPE_DISCONNECTED 2
#define MQTT_EVENT_TYPE_SUBSCRIBED 3
#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4
#define MQTT_EVENT_TYPE_PUBLISH 5
#define MQTT_EVENT_TYPE_PUBLISHED 6
#define MQTT_EVENT_TYPE_EXITED 7
#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8
void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security);
bool ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession);
void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient);
void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain);
void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb);
void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb);
void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb);
void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb);
void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb);
bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos);
bool ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic);
void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient);
void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient);
bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain);
#endif /* USER_AT_MQTT_H_ */

View File

@ -1,487 +0,0 @@
/*
* Copyright (c) 2014, Stephen Robinson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include "mqtt_msg.h"
#include "user_config.h"
#define MQTT_MAX_FIXED_HEADER_SIZE 3
enum mqtt_connect_flag
{
MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
MQTT_CONNECT_FLAG_WILL = 1 << 2,
MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
};
struct __attribute((__packed__)) mqtt_connect_variable_header
{
uint8_t lengthMsb;
uint8_t lengthLsb;
#if defined(PROTOCOL_NAMEv31)
uint8_t magic[6];
#elif defined(PROTOCOL_NAMEv311)
uint8_t magic[4];
#else
#error "Please define protocol name"
#endif
uint8_t version;
uint8_t flags;
uint8_t keepaliveMsb;
uint8_t keepaliveLsb;
};
static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len)
{
if (connection->message.length + len + 2 > connection->buffer_length)
return -1;
connection->buffer[connection->message.length++] = len >> 8;
connection->buffer[connection->message.length++] = len & 0xff;
memcpy(connection->buffer + connection->message.length, string, len);
connection->message.length += len;
return len + 2;
}
static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id)
{
// If message_id is zero then we should assign one, otherwise
// we'll use the one supplied by the caller
while (message_id == 0)
message_id = ++connection->message_id;
if (connection->message.length + 2 > connection->buffer_length)
return 0;
connection->buffer[connection->message.length++] = message_id >> 8;
connection->buffer[connection->message.length++] = message_id & 0xff;
return message_id;
}
static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection)
{
connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE;
return MQTT_MAX_FIXED_HEADER_SIZE;
}
static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection)
{
connection->message.data = connection->buffer;
connection->message.length = 0;
return &connection->message;
}
static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain)
{
int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE;
if (remaining_length > 127)
{
connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
connection->buffer[1] = 0x80 | (remaining_length % 128);
connection->buffer[2] = remaining_length / 128;
connection->message.length = remaining_length + 3;
connection->message.data = connection->buffer;
}
else
{
connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1);
connection->buffer[2] = remaining_length;
connection->message.length = remaining_length + 2;
connection->message.data = connection->buffer + 1;
}
return &connection->message;
}
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length)
{
memset(connection, 0, sizeof(mqtt_connection_t));
connection->buffer = buffer;
connection->buffer_length = buffer_length;
}
int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length)
{
int i;
int totlen = 0;
for (i = 1; i < length; ++i)
{
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
if ((buffer[i] & 0x80) == 0)
{
++i;
break;
}
}
totlen += i;
return totlen;
}
const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length)
{
int i;
int totlen = 0;
int topiclen;
for (i = 1; i < *length; ++i)
{
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
if ((buffer[i] & 0x80) == 0)
{
++i;
break;
}
}
totlen += i;
if (i + 2 >= *length)
return NULL;
topiclen = buffer[i++] << 8;
topiclen |= buffer[i++];
if (i + topiclen > *length)
return NULL;
*length = topiclen;
return (const char*)(buffer + i);
}
const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length)
{
int i;
int totlen = 0;
int topiclen;
int blength = *length;
*length = 0;
for (i = 1; i < blength; ++i)
{
totlen += (buffer[i] & 0x7f) << (7 * (i - 1));
if ((buffer[i] & 0x80) == 0)
{
++i;
break;
}
}
totlen += i;
if (i + 2 >= blength)
return NULL;
topiclen = buffer[i++] << 8;
topiclen |= buffer[i++];
if (i + topiclen >= blength)
return NULL;
i += topiclen;
if (mqtt_get_qos(buffer) > 0)
{
if (i + 2 >= blength)
return NULL;
i += 2;
}
if (totlen < i)
return NULL;
if (totlen <= blength)
*length = totlen - i;
else
*length = blength - i;
return (const char*)(buffer + i);
}
uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length)
{
if (length < 1)
return 0;
switch (mqtt_get_type(buffer))
{
case MQTT_MSG_TYPE_PUBLISH:
{
int i;
int topiclen;
for (i = 1; i < length; ++i)
{
if ((buffer[i] & 0x80) == 0)
{
++i;
break;
}
}
if (i + 2 >= length)
return 0;
topiclen = buffer[i++] << 8;
topiclen |= buffer[i++];
if (i + topiclen >= length)
return 0;
i += topiclen;
if (mqtt_get_qos(buffer) > 0)
{
if (i + 2 >= length)
return 0;
//i += 2;
} else {
return 0;
}
return (buffer[i] << 8) | buffer[i + 1];
}
case MQTT_MSG_TYPE_PUBACK:
case MQTT_MSG_TYPE_PUBREC:
case MQTT_MSG_TYPE_PUBREL:
case MQTT_MSG_TYPE_PUBCOMP:
case MQTT_MSG_TYPE_SUBACK:
case MQTT_MSG_TYPE_UNSUBACK:
case MQTT_MSG_TYPE_SUBSCRIBE:
{
// This requires the remaining length to be encoded in 1 byte,
// which it should be.
if (length >= 4 && (buffer[1] & 0x80) == 0)
return (buffer[2] << 8) | buffer[3];
else
return 0;
}
default:
return 0;
}
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info)
{
struct mqtt_connect_variable_header* variable_header;
init_message(connection);
if (connection->message.length + sizeof(*variable_header) > connection->buffer_length)
return fail_message(connection);
variable_header = (void*)(connection->buffer + connection->message.length);
connection->message.length += sizeof(*variable_header);
variable_header->lengthMsb = 0;
#if defined(PROTOCOL_NAMEv31)
variable_header->lengthLsb = 6;
memcpy(variable_header->magic, "MQIsdp", 6);
variable_header->version = 3;
#elif defined(PROTOCOL_NAMEv311)
variable_header->lengthLsb = 4;
memcpy(variable_header->magic, "MQTT", 4);
variable_header->version = 4;
#else
#error "Please define protocol name"
#endif
variable_header->flags = 0;
variable_header->keepaliveMsb = info->keepalive >> 8;
variable_header->keepaliveLsb = info->keepalive & 0xff;
if (info->clean_session)
variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
if (info->client_id == NULL)
{
/* Never allowed */
return fail_message(connection);
}
else if (info->client_id[0] == '\0')
{
#ifdef PROTOCOL_NAMEv311
/* Allowed. Format 0 Length ID */
append_string(connection, info->client_id, 2) ;
#else
/* 0 Length not allowed */
return fail_message(connection);
#endif
}
else
{
/* No 0 data and at least 1 long. Good to go. */
if(append_string(connection, info->client_id, strlen(info->client_id)) < 0)
return fail_message(connection);
}
if (info->will_topic != NULL && info->will_topic[0] != '\0')
{
if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0)
return fail_message(connection);
if (append_string(connection, info->will_message, strlen(info->will_message)) < 0)
return fail_message(connection);
variable_header->flags |= MQTT_CONNECT_FLAG_WILL;
if (info->will_retain)
variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
variable_header->flags |= (info->will_qos & 3) << 3;
}
if (info->username != NULL && info->username[0] != '\0')
{
if (append_string(connection, info->username, strlen(info->username)) < 0)
return fail_message(connection);
variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME;
}
if (info->password != NULL && info->password[0] != '\0')
{
if (append_string(connection, info->password, strlen(info->password)) < 0)
return fail_message(connection);
variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD;
}
return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id)
{
init_message(connection);
if (topic == NULL || topic[0] == '\0')
return fail_message(connection);
if (append_string(connection, topic, strlen(topic)) < 0)
return fail_message(connection);
if (qos > 0)
{
if ((*message_id = append_message_id(connection, 0)) == 0)
return fail_message(connection);
}
else
*message_id = 0;
if (connection->message.length + data_length > connection->buffer_length)
return fail_message(connection);
memcpy(connection->buffer + connection->message.length, data, data_length);
connection->message.length += data_length;
return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id)
{
init_message(connection);
if (append_message_id(connection, message_id) == 0)
return fail_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id)
{
init_message(connection);
if (append_message_id(connection, message_id) == 0)
return fail_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id)
{
init_message(connection);
if (append_message_id(connection, message_id) == 0)
return fail_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id)
{
init_message(connection);
if (append_message_id(connection, message_id) == 0)
return fail_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id)
{
init_message(connection);
if (topic == NULL || topic[0] == '\0')
return fail_message(connection);
if ((*message_id = append_message_id(connection, 0)) == 0)
return fail_message(connection);
if (append_string(connection, topic, strlen(topic)) < 0)
return fail_message(connection);
if (connection->message.length + 1 > connection->buffer_length)
return fail_message(connection);
connection->buffer[connection->message.length++] = qos;
return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id)
{
init_message(connection);
if (topic == NULL || topic[0] == '\0')
return fail_message(connection);
if ((*message_id = append_message_id(connection, 0)) == 0)
return fail_message(connection);
if (append_string(connection, topic, strlen(topic)) < 0)
return fail_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection)
{
init_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection)
{
init_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0);
}
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection)
{
init_message(connection);
return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0);
}

View File

@ -1,141 +0,0 @@
/*
* File: mqtt_msg.h
* Author: Minh Tuan
*
* Created on July 12, 2014, 1:05 PM
*/
#ifndef MQTT_MSG_H
#define MQTT_MSG_H
#include "user_config.h"
#include "c_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Copyright (c) 2014, Stephen Robinson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/* 7 6 5 4 3 2 1 0*/
/*| --- Message Type---- | DUP Flag | QoS Level | Retain |
/* Remaining Length */
enum mqtt_message_type
{
MQTT_MSG_TYPE_CONNECT = 1,
MQTT_MSG_TYPE_CONNACK = 2,
MQTT_MSG_TYPE_PUBLISH = 3,
MQTT_MSG_TYPE_PUBACK = 4,
MQTT_MSG_TYPE_PUBREC = 5,
MQTT_MSG_TYPE_PUBREL = 6,
MQTT_MSG_TYPE_PUBCOMP = 7,
MQTT_MSG_TYPE_SUBSCRIBE = 8,
MQTT_MSG_TYPE_SUBACK = 9,
MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
MQTT_MSG_TYPE_UNSUBACK = 11,
MQTT_MSG_TYPE_PINGREQ = 12,
MQTT_MSG_TYPE_PINGRESP = 13,
MQTT_MSG_TYPE_DISCONNECT = 14
};
enum mqtt_connect_return_code
{
CONNECTION_ACCEPTED = 0,
CONNECTION_REFUSE_PROTOCOL,
CONNECTION_REFUSE_ID_REJECTED,
CONNECTION_REFUSE_SERVER_UNAVAILABLE,
CONNECTION_REFUSE_BAD_USERNAME,
CONNECTION_REFUSE_NOT_AUTHORIZED
};
typedef struct mqtt_message
{
uint8_t* data;
uint16_t length;
} mqtt_message_t;
typedef struct mqtt_connection
{
mqtt_message_t message;
uint16_t message_id;
uint8_t* buffer;
uint16_t buffer_length;
} mqtt_connection_t;
typedef struct mqtt_connect_info
{
char* client_id;
char* username;
char* password;
char* will_topic;
char* will_message;
uint32_t keepalive;
int will_qos;
int will_retain;
int clean_session;
} mqtt_connect_info_t;
static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; }
static inline int ICACHE_FLASH_ATTR mqtt_get_connect_return_code(uint8_t* buffer) { return buffer[3]; }
static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; }
static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; }
static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); }
void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length);
int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length);
const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length);
const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length);
uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection);
mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection);
#ifdef __cplusplus
}
#endif
#endif /* MQTT_MSG_H */

View File

@ -1,129 +0,0 @@
#include "proto.h"
#include "ringbuf.h"
I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize)
{
parser->buf = buf;
parser->bufSize = bufSize;
parser->dataLen = 0;
parser->callback = completeCallback;
parser->isEsc = 0;
return 0;
}
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value)
{
switch (value) {
case 0x7D:
parser->isEsc = 1;
break;
case 0x7E:
parser->dataLen = 0;
parser->isEsc = 0;
parser->isBegin = 1;
break;
case 0x7F:
if (parser->callback != NULL)
parser->callback();
parser->isBegin = 0;
return 0;
break;
default:
if (parser->isBegin == 0) break;
if (parser->isEsc) {
value ^= 0x20;
parser->isEsc = 0;
}
if (parser->dataLen < parser->bufSize)
parser->buf[parser->dataLen++] = value;
break;
}
return -1;
}
I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len)
{
while (len--)
PROTO_ParseByte(parser, *buf++);
return 0;
}
I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen)
{
U8 c;
PROTO_PARSER proto;
PROTO_Init(&proto, NULL, bufOut, maxBufLen);
while (RINGBUF_Get(rb, &c) == 0) {
if (PROTO_ParseByte(&proto, c) == 0) {
*len = proto.dataLen;
return 0;
}
}
return -1;
}
I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize)
{
U16 i = 2;
U16 len = *(U16*) packet;
if (bufSize < 1) return -1;
*buf++ = 0x7E;
bufSize--;
while (len--) {
switch (*packet) {
case 0x7D:
case 0x7E:
case 0x7F:
if (bufSize < 2) return -1;
*buf++ = 0x7D;
*buf++ = *packet++ ^ 0x20;
i += 2;
bufSize -= 2;
break;
default:
if (bufSize < 1) return -1;
*buf++ = *packet++;
i++;
bufSize--;
break;
}
}
if (bufSize < 1) return -1;
*buf++ = 0x7F;
return i;
}
I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len)
{
U16 i = 2;
if (RINGBUF_Put(rb, 0x7E) == -1) return -1;
while (len--) {
switch (*packet) {
case 0x7D:
case 0x7E:
case 0x7F:
if (RINGBUF_Put(rb, 0x7D) == -1) return -1;
if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1;
i += 2;
break;
default:
if (RINGBUF_Put(rb, *packet++) == -1) return -1;
i++;
break;
}
}
if (RINGBUF_Put(rb, 0x7F) == -1) return -1;
return i;
}

View File

@ -1,32 +0,0 @@
/*
* File: proto.h
* Author: ThuHien
*
* Created on November 23, 2012, 8:57 AM
*/
#ifndef _PROTO_H_
#define _PROTO_H_
#include <stdlib.h>
#include "typedef.h"
#include "ringbuf.h"
typedef void(PROTO_PARSE_CALLBACK)();
typedef struct {
U8 *buf;
U16 bufSize;
U16 dataLen;
U8 isEsc;
U8 isBegin;
PROTO_PARSE_CALLBACK* callback;
} PROTO_PARSER;
I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize);
I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len);
I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize);
I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len);
I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value);
I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen);
#endif

View File

@ -1,75 +0,0 @@
/* str_queue.c
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "queue.h"
#include "user_interface.h"
#include "osapi.h"
#include "os_type.h"
#include "mem.h"
#include "proto.h"
uint8_t *last_rb_p_r;
uint8_t *last_rb_p_w;
uint32_t last_fill_cnt;
void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize)
{
queue->buf = (uint8_t*)os_zalloc(bufferSize);
RINGBUF_Init(&queue->rb, queue->buf, bufferSize);
}
int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len)
{
uint32_t ret;
last_rb_p_r = queue->rb.p_r;
last_rb_p_w = queue->rb.p_w;
last_fill_cnt = queue->rb.fill_cnt;
ret = PROTO_AddRb(&queue->rb, buffer, len);
if (ret == -1) {
// rolling ring buffer back
queue->rb.p_r = last_rb_p_r;
queue->rb.p_w = last_rb_p_w;
queue->rb.fill_cnt = last_fill_cnt;
}
return ret;
}
int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen)
{
return PROTO_ParseRb(&queue->rb, buffer, len, maxLen);
}
bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue)
{
if (queue->rb.fill_cnt <= 0)
return TRUE;
return FALSE;
}

View File

@ -1,44 +0,0 @@
/* str_queue.h --
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef USER_QUEUE_H_
#define USER_QUEUE_H_
#include "os_type.h"
#include "ringbuf.h"
typedef struct {
uint8_t *buf;
RINGBUF rb;
} QUEUE;
void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize);
int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len);
int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen);
bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue);
#endif /* USER_QUEUE_H_ */

View File

@ -1,67 +0,0 @@
/**
* \file
* Ring Buffer library
*/
#include "ringbuf.h"
/**
* \brief init a RINGBUF object
* \param r pointer to a RINGBUF object
* \param buf pointer to a byte array
* \param size size of buf
* \return 0 if successfull, otherwise failed
*/
I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size)
{
if (r == NULL || buf == NULL || size < 2) return -1;
r->p_o = r->p_r = r->p_w = buf;
r->fill_cnt = 0;
r->size = size;
return 0;
}
/**
* \brief put a character into ring buffer
* \param r pointer to a ringbuf object
* \param c character to be put
* \return 0 if successfull, otherwise failed
*/
I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c)
{
if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation
r->fill_cnt++; // increase filled slots count, this should be atomic operation
*r->p_w++ = c; // put character into buffer
if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass
r->p_w = r->p_o; // the physical boundary
return 0;
}
/**
* \brief get a character from ring buffer
* \param r pointer to a ringbuf object
* \param c read character
* \return 0 if successfull, otherwise failed
*/
I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c)
{
if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation
r->fill_cnt--; // decrease filled slots count
*c = *r->p_r++; // get the character out
if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass
r->p_r = r->p_o; // the physical boundary
return 0;
}

View File

@ -1,19 +0,0 @@
#ifndef _RING_BUF_H_
#define _RING_BUF_H_
#include <os_type.h>
#include <stdlib.h>
#include "typedef.h"
typedef struct {
U8* p_o; /**< Original pointer */
U8* volatile p_r; /**< Read pointer */
U8* volatile p_w; /**< Write pointer */
volatile I32 fill_cnt; /**< Number of filled slots */
I32 size; /**< Buffer size */
} RINGBUF;
I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size);
I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c);
I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c);
#endif

View File

@ -1,17 +0,0 @@
/**
* \file
* Standard Types definition
*/
#ifndef _TYPE_DEF_H_
#define _TYPE_DEF_H_
typedef char I8;
typedef unsigned char U8;
typedef short I16;
typedef unsigned short U16;
typedef long I32;
typedef unsigned long U32;
typedef unsigned long long U64;
#endif

View File

@ -1,15 +0,0 @@
#ifndef __MQTT_CONFIG_H__
#define __MQTT_CONFIG_H__
//#define MQTT_SSL_ENABLE
#define MQTT_RECONNECT_TIMEOUT 5 /*second*/
//#define MQTT_BUF_SIZE 1024
#define MQTT_BUF_SIZE 512
#define QUEUE_BUFFER_SIZE 2048
//#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/
#define PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/
#endif // __MQTT_CONFIG_H__

View File

@ -1,149 +0,0 @@
/*
* Copyright (c) 2014, Tuan PM
* Email: tuanpm@live.com
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stddef.h>
#include "utils.h"
uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str)
{
uint8_t segs = 0; /* Segment count. */
uint8_t chcnt = 0; /* Character count within segment. */
uint8_t accum = 0; /* Accumulator for segment. */
/* Catch NULL pointer. */
if (str == 0)
return 0;
/* Process every character in string. */
while (*str != '\0') {
/* Segment changeover. */
if (*str == '.') {
/* Must have some digits in segment. */
if (chcnt == 0)
return 0;
/* Limit number of segments. */
if (++segs == 4)
return 0;
/* Reset segment values and restart loop. */
chcnt = accum = 0;
str++;
continue;
}
/* Check numeric. */
if ((*str < '0') || (*str > '9'))
return 0;
/* Accumulate and check segment. */
if ((accum = accum * 10 + *str - '0') > 255)
return 0;
/* Advance other segment specific stuff and continue loop. */
chcnt++;
str++;
}
/* Check enough segments and enough characters in last segment. */
if (segs != 3)
return 0;
if (chcnt == 0)
return 0;
/* Address okay. */
return 1;
}
uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip)
{
/* The count of the number of bytes processed. */
int i;
/* A pointer to the next digit to process. */
const char * start;
start = str;
for (i = 0; i < 4; i++) {
/* The digit being processed. */
char c;
/* The value of this byte. */
int n = 0;
while (1) {
c = * start;
start++;
if (c >= '0' && c <= '9') {
n *= 10;
n += c - '0';
}
/* We insist on stopping at "." if we are still parsing
the first, second, or third numbers. If we have reached
the end of the numbers, we will allow any character. */
else if ((i < 3 && c == '.') || i == 3) {
break;
}
else {
return 0;
}
}
if (n >= 256) {
return 0;
}
((uint8_t*)ip)[i] = n;
}
return 1;
}
uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s)
{
uint32_t value = 0, digit;
int8_t c;
while ((c = *s++)) {
if ('0' <= c && c <= '9')
digit = c - '0';
else if ('A' <= c && c <= 'F')
digit = c - 'A' + 10;
else if ('a' <= c && c <= 'f')
digit = c - 'a' + 10;
else break;
value = (value << 4) | digit;
}
return value;
}

View File

@ -1,9 +0,0 @@
#ifndef _UTILS_H_
#define _UTILS_H_
#include "c_types.h"
uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s);
uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip);
uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str);
#endif

View File

@ -1,7 +0,0 @@
[Makefile]
indent_style = tab
indent_size = 4
[src/*.h,src/*.cpp,examples/**.ino]
indent_style = space
indent_size = 2

View File

@ -1,2 +0,0 @@
.DS_Store
cmake-build-debug/

View File

@ -1,46 +0,0 @@
language: generic
env:
global:
- IDE_VERSION=1.8.7
matrix:
- EXAMPLE="AdafruitHuzzahESP8266" BOARD="esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80"
- EXAMPLE="AdafruitHuzzahESP8266Secure" BOARD="esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80"
- EXAMPLE="ArduinoEthernetShield" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoMKRGSM1400" BOARD="arduino:samd:mkrgsm1400"
- EXAMPLE="ArduinoMKRGSM1400Secure" BOARD="arduino:samd:mkrgsm1400"
- EXAMPLE="ArduinoWiFi101Secure" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoWiFiShield" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoYun" BOARD="arduino:avr:yun"
- EXAMPLE="ArduinoYunSecure" BOARD="arduino:avr:yun"
- EXAMPLE="ESP32DevelopmentBoard" BOARD="espressif:esp32:esp32:FlashFreq=80"
- EXAMPLE="ESP32DevelopmentBoardSecure" BOARD="espressif:esp32:esp32:FlashFreq=80"
before_install:
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- sleep 3
- export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-$IDE_VERSION ~/arduino-ide
- export PATH=$PATH:~/arduino-ide
- if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then
arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --install-boards esp8266:esp8266;
arduino --pref "boardsmanager.additional.urls=" --save-prefs;
fi
- if [[ "$BOARD" =~ "espressif:esp32:" ]]; then
mkdir -p ~/Arduino/hardware/espressif &&
cd ~/Arduino/hardware/espressif &&
git clone https://github.com/espressif/arduino-esp32.git esp32 &&
cd esp32/tools/ &&
python get.py &&
cd $TRAVIS_BUILD_DIR;
fi
- if [[ "$BOARD" =~ "arduino:samd:mkrgsm1400" ]]; then
arduino --install-boards arduino:samd;
arduino --install-library MKRGSM;
fi
- arduino --install-library WiFi101
install:
- mkdir -p ~/Arduino/libraries
- ln -s $PWD ~/Arduino/libraries/.
script:
- arduino --verbose-build --verify --board $BOARD $PWD/examples/$EXAMPLE/$EXAMPLE.ino;

View File

@ -1,38 +0,0 @@
# Uncompilable CMake File to enable project editing with CLion IDE
cmake_minimum_required(VERSION 2.8.4)
project(arduino-mqtt)
include_directories(
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/
/Users/256dpi/Development/Arduino/libraries/Ethernet/src
/Users/256dpi/Development/Arduino/libraries/WiFi101/src
/Users/256dpi/Development/Arduino/libraries/MKRGSM/src
/Applications/Arduino.app/Contents/Java/libraries/Bridge/src
/Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFiClientSecure/src
src/)
include_directories(src/)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES
examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino
examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino
examples/ArduinoEthernetShield/ArduinoEthernetShield.ino
examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino
examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino
examples/ArduinoWiFi101/ArduinoWiFi101.ino
examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino
examples/ArduinoWiFiShield/ArduinoWiFiShield.ino
examples/ArduinoYun/ArduinoYun.ino
examples/ArduinoYunSecure/ArduinoYunSecure.ino
examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino
examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino
src/lwmqtt
src/MQTT.h
src/MQTTClient.h)
add_executable(arduino-mqtt ${SOURCE_FILES})

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Joël Gähwiler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,14 +0,0 @@
all: fmt
fmt:
clang-format -i src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}"
update:
rm -rf ./lwmqtt
git clone --branch v0.6.2 https://github.com/256dpi/lwmqtt.git ./lwmqtt
mkdir -p ./src/lwmqtt
cp -r ./lwmqtt/src/*.c ./src/lwmqtt/
cp -r ./lwmqtt/src/*.h ./src/lwmqtt/
cp -r ./lwmqtt/include/*.h ./src/lwmqtt/
rm -rf ./lwmqtt
sed -i '' "s/<lwmqtt.h>/\"lwmqtt.h\"/g" ./src/lwmqtt/*

View File

@ -1,226 +0,0 @@
# arduino-mqtt
[![Build Status](https://travis-ci.org/256dpi/arduino-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/arduino-mqtt)
[![GitHub release](https://img.shields.io/github/release/256dpi/arduino-mqtt.svg)](https://github.com/256dpi/arduino-mqtt/releases)
This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) MQTT 3.1.1 client and adds a thin wrapper to get an Arduino like API.
Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT".
The library is also available on [PlatformIO](https://platformio.org/lib/show/617/MQTT). You can install it by running: `pio lib install "MQTT"`.
## Compatibility
The following examples show how you can use the library with various Arduino compatible hardware:
- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYunSecure/ArduinoYunSecure.ino))
- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino)
- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino)
- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino))
- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino))
- [Arduino MKR GSM 1400](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino))
- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino))
Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation.
## Notes
- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size.
- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections.
- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`.
## Example
The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try.
```c++
#include <SPI.h>
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}
```
## API
Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport:
```c++
void begin(const char hostname[], Client &client);
void begin(const char hostname[], int port, Client &client);
```
- Specify port `8883` when using secure clients for encrypted connections.
- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly.
The hostname and port can also be changed after calling `begin()`:
```c++
void setHost(const char hostname[]);
void setHost(const char hostname[], int port);
```
Set a will message (last testament) that gets registered on the broker after connecting. `setWill()` has to be called before calling `connect()`:
```c++
void setWill(const char topic[]);
void setWill(const char topic[], const char payload[]);
void setWill(const char topic[], const char payload[], bool retained, int qos);
void clearWill();
```
Register a callback to receive messages:
```c++
void onMessage(MQTTClientCallbackSimple);
// Callback signature: void messageReceived(String &topic, String &payload) {}
void onMessageAdvanced(MQTTClientCallbackAdvanced);
// Callback signature: void messageReceived(MQTTClient *client, char topic[], char payload[], int payload_length) {}
```
- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback.
Set more advanced options:
```c++
void setOptions(int keepAlive, bool cleanSession, int timeout);
```
- The `keepAlive` option controls the keep alive interval in seconds (default: 10).
- The `cleanSession` option controls the session retention on the broker side (default: true).
- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000).
Connect to broker using the supplied client id and an optional username and password:
```c++
bool connect(const char clientId[], bool skip = false);
bool connect(const char clientId[], const char username[], bool skip = false);
bool connect(const char clientId[], const char username[], const char password[], bool skip = false);
```
- If the `skip` option is set to true, the client will skip the network level connection and jump to the MQTT level connection. This option can be used in order to establish and verify TLS connections manually before giving control to the MQTT client.
- This functions returns a boolean that indicates if the connection has been established successfully.
Publishes a message to the broker with an optional payload:
```c++
bool publish(const String &topic);
bool publish(const char topic[]);
bool publish(const String &topic, const String &payload);
bool publish(const String &topic, const String &payload, bool retained, int qos);
bool publish(const char topic[], const String &payload);
bool publish(const char topic[], const String &payload, bool retained, int qos);
bool publish(const char topic[], const char payload[]);
bool publish(const char topic[], const char payload[], bool retained, int qos);
bool publish(const char topic[], const char payload[], int length);
bool publish(const char topic[], const char payload[], int length, bool retained, int qos);
```
Subscribe to a topic:
```c++
bool subscribe(const String &topic);
bool subscribe(const String &topic, int qos);
bool subscribe(const char topic[]);
bool subscribe(const char topic[], int qos);
```
Unsubscribe from a topic:
```c++
bool unsubscribe(const String &topic);
bool unsubscribe(const char topic[]);
```
Sends and receives packets:
```c++
bool loop();
```
- This function should be called in every `loop`.
Check if the client is currently connected:
```c++
bool connected();
```
Access low-level information for debugging:
```c++
lwmqtt_err_t lastError();
lwmqtt_return_code_t returnCode();
```
- The error codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11).
- The return codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L243).
Disconnect from the broker:
```c++
bool disconnect();
```
## Release Management
- Update version in `library.properties`.
- Create release on GitHub.

View File

@ -1,69 +0,0 @@
// This example uses an Adafruit Huzzah ESP8266
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <ESP8266WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,71 +0,0 @@
// This example uses an Adafruit Huzzah ESP8266
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <ESP8266WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClientSecure net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,62 +0,0 @@
// This example uses an Arduino Uno together with
// an Ethernet Shield to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Ethernet.h>
#include <MQTT.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {192, 168, 1, 177}; // <- change to match your network
EthernetClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
Ethernet.begin(mac, ip);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,84 +0,0 @@
// This example uses an Arduino MKR GSM 1400 board
// to connect to shiftr.io.
//
// IMPORTANT: This example uses the new MKRGSM library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Sandeep Mistry
// https://github.com/256dpi/arduino-mqtt
#include <MKRGSM.h>
#include <MQTT.h>
const char pin[] = "";
const char apn[] = "apn";
const char login[] = "login";
const char password[] = "password";
GSMClient net;
GPRS gprs;
GSM gsmAccess;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
// connection state
bool connected = false;
Serial.print("connecting to cellular network ...");
// After starting the modem with gsmAccess.begin()
// attach to the GPRS network with the APN, login and password
while (!connected) {
if ((gsmAccess.begin(pin) == GSM_READY) &&
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
connected = true;
} else {
Serial.print(".");
delay(1000);
}
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,86 +0,0 @@
// This example uses an Arduino MKR GSM 1400 board
// to securely connect to shiftr.io.
//
// IMPORTANT: This example uses the new MKRGSM library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Sandeep Mistry
// https://github.com/256dpi/arduino-mqtt
#include <MKRGSM.h>
#include <MQTT.h>
const char pin[] = "";
const char apn[] = "apn";
const char login[] = "login";
const char password[] = "password";
GSMSSLClient net;
GPRS gprs;
GSM gsmAccess;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
// connection state
bool connected = false;
Serial.print("connecting to cellular network ...");
// After starting the modem with gsmAccess.begin()
// attach to the GPRS network with the APN, login and password
while (!connected) {
if ((gsmAccess.begin(pin) == GSM_READY) &&
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
connected = true;
} else {
Serial.print(".");
delay(1000);
}
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,70 +0,0 @@
// This example uses an Arduino/Genuino Zero together with
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
//
// IMPORTANT: This example uses the new WiFi101 library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Gilberto Conti
// https://github.com/256dpi/arduino-mqtt
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,75 +0,0 @@
// This example uses an Arduino/Genuino Zero together with
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
//
// IMPORTANT: This example uses the new WiFi101 library.
//
// IMPORTANT: You need to install/update the SSL certificates first:
// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Gilberto Conti
// https://github.com/256dpi/arduino-mqtt
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiSSLClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,68 +0,0 @@
// This example uses an Arduino Uno together with
// a WiFi Shield to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,60 +0,0 @@
// This example uses an Arduino Yun or a Yun-Shield
// and the MQTTClient to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Bridge.h>
#include <BridgeClient.h>
#include <MQTT.h>
BridgeClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Bridge.begin();
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,62 +0,0 @@
// This example uses an Arduino Yun or a Yun-Shield
// and the MQTTClient to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Bridge.h>
#include <BridgeSSLClient.h>
#include <MQTT.h>
BridgeSSLClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Bridge.begin();
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,69 +0,0 @@
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,71 +0,0 @@
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFiClientSecure.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClientSecure net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@ -1,9 +0,0 @@
name=MQTT
version=2.4.0
author=Joel Gaehwiler <joel.gaehwiler@gmail.com>
maintainer=Joel Gaehwiler <joel.gaehwiler@gmail.com>
sentence=MQTT library for Arduino
paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API.
category=Communication
url=https://github.com/256dpi/arduino-mqtt
architectures=*

View File

@ -1,6 +0,0 @@
#ifndef MQTT_H
#define MQTT_H
#include "MQTTClient.h"
#endif

View File

@ -1,491 +0,0 @@
#ifndef MQTT_CLIENT_H
#define MQTT_CLIENT_H
#include <Arduino.h>
#include <Client.h>
#include <Stream.h>
extern "C" {
#include "lwmqtt/lwmqtt.h"
};
typedef struct {
uint32_t end;
} lwmqtt_arduino_timer_t;
void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout);
int32_t lwmqtt_arduino_timer_get(void *ref);
typedef struct {
Client *client;
} lwmqtt_arduino_network_t;
void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) {
// cast timer reference
auto t = (lwmqtt_arduino_timer_t *)ref;
// set future end time
t->end = (uint32_t)(millis() + timeout);
}
int32_t lwmqtt_arduino_timer_get(void *ref) {
// cast timer reference
auto t = (lwmqtt_arduino_timer_t *)ref;
// get difference to end time
return (int32_t)t->end - (int32_t)millis();
}
lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read, uint32_t timeout) {
// cast network reference
auto n = (lwmqtt_arduino_network_t *)ref;
// set timeout
n->client->setTimeout(timeout);
// read bytes
*read = n->client->readBytes(buffer, len);
if (*read <= 0) {
return LWMQTT_NETWORK_FAILED_READ;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent, uint32_t /*timeout*/) {
// cast network reference
auto n = (lwmqtt_arduino_network_t *)ref;
// write bytes
*sent = n->client->write(buffer, len);
if (*sent <= 0) {
return LWMQTT_NETWORK_FAILED_WRITE;
};
return LWMQTT_SUCCESS;
}
class MQTTClient;
typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload);
typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length);
typedef struct {
MQTTClient *client = nullptr;
MQTTClientCallbackSimple simple = nullptr;
MQTTClientCallbackAdvanced advanced = nullptr;
} MQTTClientCallback;
static void MQTTClientHandler(lwmqtt_client_t * /*client*/, void *ref, lwmqtt_string_t topic,
lwmqtt_message_t message) {
// get callback
auto cb = (MQTTClientCallback *)ref;
// null terminate topic
char terminated_topic[topic.len + 1];
memcpy(terminated_topic, topic.data, topic.len);
terminated_topic[topic.len] = '\0';
// null terminate payload if available
if (message.payload != nullptr) {
message.payload[message.payload_len] = '\0';
}
// call the advanced callback and return if available
if (cb->advanced != nullptr) {
cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len);
return;
}
// return if simple callback is not set
if (cb->simple == nullptr) {
return;
}
// create topic string
String str_topic = String(terminated_topic);
// create payload string
String str_payload;
if (message.payload != nullptr) {
str_payload = String((const char *)message.payload);
}
// call simple callback
cb->simple(str_topic, str_payload);
}
class MQTTClient {
private:
size_t bufSize = 0;
uint8_t *readBuf = nullptr;
uint8_t *writeBuf = nullptr;
uint16_t keepAlive = 10;
bool cleanSession = true;
uint32_t timeout = 1000;
Client *netClient = nullptr;
const char *hostname = nullptr;
int port = 0;
lwmqtt_will_t *will = nullptr;
MQTTClientCallback callback;
lwmqtt_arduino_network_t network = {nullptr};
lwmqtt_arduino_timer_t timer1 = {0};
lwmqtt_arduino_timer_t timer2 = {0};
lwmqtt_client_t client = {0};
bool _connected = false;
lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0;
lwmqtt_err_t _lastError = (lwmqtt_err_t)0;
public:
explicit MQTTClient(int bufSize = 128) {
// reset client
memset(&this->client, 0, sizeof(lwmqtt_client_t));
// allocate buffers
this->bufSize = (size_t)bufSize;
this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1);
this->writeBuf = (uint8_t *)malloc((size_t)bufSize);
}
~MQTTClient() {
// free will
this->clearWill();
// free hostname
if (this->hostname != nullptr) {
free((void *)this->hostname);
}
// free buffers
free(this->readBuf);
free(this->writeBuf);
}
void begin(const char hostname[], Client &client) { this->begin(hostname, 1883, client); }
void begin(const char hostname[], int port, Client &client) {
// set hostname and port
this->setHost(hostname, port);
// set client
this->netClient = &client;
// initialize client
lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize);
// set timers
lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get);
// set network
lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write);
// set callback
lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler);
}
void onMessage(MQTTClientCallbackSimple cb) {
// set callback
this->callback.client = this;
this->callback.simple = cb;
this->callback.advanced = nullptr;
}
void onMessageAdvanced(MQTTClientCallbackAdvanced cb) {
// set callback
this->callback.client = this;
this->callback.simple = nullptr;
this->callback.advanced = cb;
}
void setHost(const char hostname[]) { this->setHost(hostname, 1883); }
void setHost(const char hostname[], int port) {
// free hostname if set
if (this->hostname != nullptr) {
free((void *)this->hostname);
}
// set hostname and port
this->hostname = strdup(hostname);
this->port = port;
}
void setWill(const char topic[]) { this->setWill(topic, ""); }
void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); }
void setWill(const char topic[], const char payload[], bool retained, int qos) {
// return if topic is missing
if (topic == nullptr || strlen(topic) == 0) {
return;
}
// clear existing will
this->clearWill();
// allocate will
this->will = (lwmqtt_will_t *)malloc(sizeof(lwmqtt_will_t));
memset(this->will, 0, sizeof(lwmqtt_will_t));
// set topic
this->will->topic = lwmqtt_string(strdup(topic));
// set payload if available
if (payload != nullptr && strlen(payload) > 0) {
this->will->payload = lwmqtt_string(strdup(payload));
}
// set flags
this->will->retained = retained;
this->will->qos = (lwmqtt_qos_t)qos;
}
void clearWill() {
// return if not set
if (this->will == nullptr) {
return;
}
// free payload if set
if (this->will->payload.len > 0) {
free(this->will->payload.data);
}
// free topic if set
if (this->will->topic.len > 0) {
free(this->will->topic.data);
}
// free will
free(this->will);
this->will = nullptr;
}
void setOptions(int keepAlive, bool cleanSession, int timeout) {
// set new options
this->keepAlive = (uint16_t)keepAlive;
this->cleanSession = cleanSession;
this->timeout = (uint32_t)timeout;
}
bool connect(const char clientId[], bool skip = false) { return this->connect(clientId, nullptr, nullptr); }
bool connect(const char clientId[], const char username[], bool skip = false) { return this->connect(clientId, username, nullptr); }
bool connect(const char clientId[], const char username[], const char password[], bool skip = false) {
// close left open connection if still connected
if (!skip && this->connected()) {
this->close();
}
// save client
this->network.client = this->netClient;
// connect to hostg
if(!skip) {
int ret = this->netClient->connect(this->hostname, (uint16_t)this->port);
if (ret <= 0) {
return false;
}
}
// prepare options
lwmqtt_options_t options = lwmqtt_default_options;
options.keep_alive = this->keepAlive;
options.clean_session = this->cleanSession;
options.client_id = lwmqtt_string(clientId);
// set username and password if available
if (username != nullptr) {
options.username = lwmqtt_string(username);
if (password != nullptr) {
options.password = lwmqtt_string(password);
}
}
// connect to broker
this->_lastError = lwmqtt_connect(&this->client, options, this->will, &this->_returnCode, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
// set flag
this->_connected = true;
return true;
}
bool publish(const String &topic) { return this->publish(topic.c_str(), ""); }
bool publish(const char topic[]) { return this->publish(topic, ""); }
bool publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); }
bool publish(const String &topic, const String &payload, bool retained, int qos) {
return this->publish(topic.c_str(), payload.c_str(), retained, qos);
}
bool publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); }
bool publish(const char topic[], const String &payload, bool retained, int qos) {
return this->publish(topic, payload.c_str(), retained, qos);
}
bool publish(const char topic[], const char payload[]) {
return this->publish(topic, (char *)payload, (int)strlen(payload));
}
bool publish(const char topic[], const char payload[], bool retained, int qos) {
return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos);
}
bool publish(const char topic[], const char payload[], int length) {
return this->publish(topic, payload, length, false, 0);
}
bool publish(const char topic[], const char payload[], int length, bool retained, int qos) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// prepare message
lwmqtt_message_t message = lwmqtt_default_message;
message.payload = (uint8_t *)payload;
message.payload_len = (size_t)length;
message.retained = retained;
message.qos = lwmqtt_qos_t(qos);
// publish message
this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool subscribe(const String &topic) { return this->subscribe(topic.c_str()); }
bool subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); }
bool subscribe(const char topic[]) { return this->subscribe(topic, 0); }
bool subscribe(const char topic[], int qos) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// subscribe to topic
this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); }
bool unsubscribe(const char topic[]) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// unsubscribe from topic
this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool loop() {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// get available bytes on the network
auto available = (size_t)this->netClient->available();
// yield if data is available
if (available > 0) {
this->_lastError = lwmqtt_yield(&this->client, available, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
}
// keep the connection alive
this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool connected() {
// a client is connected if the network is connected, a client is available and
// the connection has been properly initiated
return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected;
}
lwmqtt_err_t lastError() { return this->_lastError; }
lwmqtt_return_code_t returnCode() { return this->_returnCode; }
bool disconnect() {
// return immediately if not connected anymore
if (!this->connected()) {
return false;
}
// cleanly disconnect
this->_lastError = lwmqtt_disconnect(&this->client, this->timeout);
// close
this->close();
return this->_lastError == LWMQTT_SUCCESS;
}
private:
void close() {
// set flag
this->_connected = false;
// close network
this->netClient->stop();
}
};
#endif

View File

@ -1,618 +0,0 @@
#include "packet.h"
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
size_t read_buf_size) {
client->last_packet_id = 1;
client->keep_alive_interval = 0;
client->pong_pending = false;
client->write_buf = write_buf;
client->write_buf_size = write_buf_size;
client->read_buf = read_buf;
client->read_buf_size = read_buf_size;
client->callback = NULL;
client->callback_ref = NULL;
client->network = NULL;
client->network_read = NULL;
client->network_write = NULL;
client->keep_alive_timer = NULL;
client->command_timer = NULL;
client->timer_set = NULL;
client->timer_get = NULL;
}
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) {
client->network = ref;
client->network_read = read;
client->network_write = write;
}
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
lwmqtt_timer_get_t get) {
client->keep_alive_timer = keep_alive_timer;
client->command_timer = command_timer;
client->timer_set = set;
client->timer_get = get;
client->timer_set(client->keep_alive_timer, 0);
client->timer_set(client->command_timer, 0);
}
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) {
client->callback_ref = ref;
client->callback = cb;
}
static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) {
// check overflow
if (client->last_packet_id == 65535) {
client->last_packet_id = 1;
return 1;
}
// increment packet id
client->last_packet_id++;
return client->last_packet_id;
}
static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) {
// check read buffer capacity
if (client->read_buf_size < offset + len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// prepare counter
size_t read = 0;
// read while data is missing
while (read < len) {
// check remaining time
int32_t remaining_time = client->timer_get(client->command_timer);
if (remaining_time <= 0) {
return LWMQTT_NETWORK_TIMEOUT;
}
// read
size_t partial_read = 0;
lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read,
&partial_read, (uint32_t)remaining_time);
if (err != LWMQTT_SUCCESS) {
return err;
}
// increment counter
read += partial_read;
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) {
// prepare counter
size_t written = 0;
// write while data is left
while (written < len) {
// check remaining time
int32_t remaining_time = client->timer_get(client->command_timer);
if (remaining_time <= 0) {
return LWMQTT_NETWORK_TIMEOUT;
}
// write
size_t partial_write = 0;
lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written,
&partial_write, (uint32_t)remaining_time);
if (err != LWMQTT_SUCCESS) {
return err;
}
// increment counter
written += partial_write;
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read,
lwmqtt_packet_type_t *packet_type) {
// preset packet type
*packet_type = LWMQTT_NO_PACKET;
// read or wait for header byte
lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1);
if (err == LWMQTT_NETWORK_TIMEOUT) {
// this is ok as no data has been read at all
return LWMQTT_SUCCESS;
} else if (err != LWMQTT_SUCCESS) {
return err;
}
// detect packet type
err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
}
// prepare variables
size_t len = 0;
uint32_t rem_len = 0;
do {
// adjust len
len++;
// read next byte
err = lwmqtt_read_from_network(client, len, 1);
if (err != LWMQTT_SUCCESS) {
return err;
}
// attempt to detect remaining length
err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len);
} while (err == LWMQTT_BUFFER_TOO_SHORT);
// check final error
if (err != LWMQTT_SUCCESS) {
return err;
}
// read the rest of the buffer if needed
if (rem_len > 0) {
err = lwmqtt_read_from_network(client, 1 + len, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// adjust counter
*read += 1 + len + rem_len;
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) {
// write to network
lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length);
if (err != LWMQTT_SUCCESS) {
return err;
}
// reset keep alive timer
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) {
// read next packet from the network
lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (*packet_type == LWMQTT_NO_PACKET) {
return LWMQTT_SUCCESS;
}
switch (*packet_type) {
// handle publish packets
case LWMQTT_PUBLISH_PACKET: {
// decode publish packet
bool dup;
uint16_t packet_id;
lwmqtt_string_t topic;
lwmqtt_message_t msg;
err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg);
if (err != LWMQTT_SUCCESS) {
return err;
}
// call callback if set
if (client->callback != NULL) {
client->callback(client, client->callback_ref, topic, msg);
}
// break early on qos zero
if (msg.qos == LWMQTT_QOS0) {
break;
}
// define ack packet
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
if (msg.qos == LWMQTT_QOS1) {
ack_type = LWMQTT_PUBACK_PACKET;
} else if (msg.qos == LWMQTT_QOS2) {
ack_type = LWMQTT_PUBREC_PACKET;
}
// encode ack packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send ack packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pubrec packets
case LWMQTT_PUBREC_PACKET: {
// decode pubrec packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// encode pubrel packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send pubrel packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pubrel packets
case LWMQTT_PUBREL_PACKET: {
// decode pubrec packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// encode pubcomp packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send pubcomp packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pingresp packets
case LWMQTT_PINGRESP_PACKET: {
// set flag
client->pong_pending = false;
break;
}
// handle all other packets
default: { break; }
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available,
lwmqtt_packet_type_t needle) {
// prepare counter
size_t read = 0;
// loop until timeout has been reached
do {
// do one cycle
lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
}
// return when one packet has been successfully read when no availability has been given
if (needle == LWMQTT_NO_PACKET && available == 0) {
return LWMQTT_SUCCESS;
}
// otherwise check if needle has been found
if (*packet_type == needle) {
return LWMQTT_SUCCESS;
}
} while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available));
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// cycle until timeout has been reached
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
lwmqtt_return_code_t *return_code, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// save keep alive interval (take 75% to be a little earlier than actually needed)
client->keep_alive_interval = (uint32_t)(options.keep_alive * 750);
// set keep alive timer
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
// reset pong pending flag
client->pong_pending = false;
// initialize return code
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
// encode connect packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for connack packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_CONNACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode connack packet
bool session_present;
err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code);
if (err != LWMQTT_SUCCESS) {
return err;
}
// return error if connection was not accepted
if (*return_code != LWMQTT_CONNECTION_ACCEPTED) {
return LWMQTT_CONNECTION_DENIED;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode subscribe packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len,
lwmqtt_get_next_packet_id(client), count, topic_filter, qos);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for suback packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_SUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode packet
int suback_count = 0;
lwmqtt_qos_t granted_qos[count];
uint16_t packet_id;
err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check suback codes
for (int i = 0; i < suback_count; i++) {
if (granted_qos[i] == LWMQTT_QOS_FAILURE) {
return LWMQTT_FAILED_SUBSCRIPTION;
}
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
uint32_t timeout) {
return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout);
}
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode unsubscribe packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len,
lwmqtt_get_next_packet_id(client), count, topic_filter);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send unsubscribe packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for unsuback packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_UNSUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode unsuback packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) {
return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout);
}
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message,
uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// add packet id if at least qos 1
uint16_t packet_id = 0;
if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) {
packet_id = lwmqtt_get_next_packet_id(client);
}
// encode publish packet
size_t len = 0;
lwmqtt_err_t err =
lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// immediately return on qos zero
if (message.qos == LWMQTT_QOS0) {
return LWMQTT_SUCCESS;
}
// define ack packet
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
if (message.qos == LWMQTT_QOS1) {
ack_type = LWMQTT_PUBACK_PACKET;
} else if (message.qos == LWMQTT_QOS2) {
ack_type = LWMQTT_PUBCOMP_PACKET;
}
// wait for ack packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != ack_type) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode ack packet
bool dup;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode disconnect packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send disconnected packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// return immediately if keep alive interval is zero
if (client->keep_alive_interval == 0) {
return LWMQTT_SUCCESS;
}
// return immediately if no ping is due
if (client->timer_get(client->keep_alive_timer) > 0) {
return LWMQTT_SUCCESS;
}
// a ping is due
// fail immediately if a pong is already pending
if (client->pong_pending) {
return LWMQTT_PONG_TIMEOUT;
}
// encode pingreq packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set flag
client->pong_pending = true;
return LWMQTT_SUCCESS;
}

View File

@ -1,249 +0,0 @@
#include <string.h>
#include "helpers.h"
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; }
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) {
*byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos);
}
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) {
// check zero length
if (len == 0) {
*data = NULL;
return LWMQTT_SUCCESS;
}
// check buffer size
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// read data
*data = *buf;
// advance pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) {
// check zero length
if (len == 0) {
return LWMQTT_SUCCESS;
}
// check buffer size
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write data
memcpy(*buf, data, len);
// advance pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 2) {
*num = 0;
return LWMQTT_BUFFER_TOO_SHORT;
}
// read two byte integer
*num = (uint16_t)256 * (*buf)[0] + (*buf)[1];
// adjust pointer
*buf += 2;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 2) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write bytes
(*buf)[0] = (uint8_t)(num / 256);
(*buf)[1] = (uint8_t)(num % 256);
// adjust pointer
*buf += 2;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) {
// read length
uint16_t len;
lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read data
err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
str->len = len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) {
// write string length
lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write data
err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 1) {
*byte = 0;
return LWMQTT_BUFFER_TOO_SHORT;
}
// read byte
*byte = (*buf)[0];
// adjust pointer
*buf += 1;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 1) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write byte
(*buf)[0] = byte;
// adjust pointer
*buf += 1;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) {
if (varnum < 128) {
*len = 1;
return LWMQTT_SUCCESS;
} else if (varnum < 16384) {
*len = 2;
return LWMQTT_SUCCESS;
} else if (varnum < 2097151) {
*len = 3;
return LWMQTT_SUCCESS;
} else if (varnum < 268435455) {
*len = 4;
return LWMQTT_SUCCESS;
} else {
*len = 0;
return LWMQTT_VARNUM_OVERFLOW;
}
}
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) {
// prepare last byte
uint8_t byte;
// prepare multiplier
uint32_t multiplier = 1;
// prepare length
size_t len = 0;
// initialize number
*varnum = 0;
// decode variadic number
do {
// increment length
len++;
// return error if buffer is to small
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// return error if the length has overflowed
if (len > 4) {
return LWMQTT_VARNUM_OVERFLOW;
}
// read byte
byte = (*buf)[len - 1];
// add byte to number
*varnum += (byte & 127) * multiplier;
// increase multiplier
multiplier *= 128;
} while ((byte & 128) != 0);
// adjust pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) {
// init len counter
size_t len = 0;
// encode variadic number
do {
// check overflow
if (len == 4) {
return LWMQTT_VARNUM_OVERFLOW;
}
// return error if buffer is to small
if ((size_t)(buf_end - (*buf)) < len + 1) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// calculate current byte
uint8_t byte = (uint8_t)(varnum % 128);
// change remaining length
varnum /= 128;
// set the top bit of this byte if there are more to encode
if (varnum > 0) {
byte |= 0x80;
}
// write byte
(*buf)[len++] = byte;
} while (varnum > 0);
// adjust pointer
*buf += len;
return LWMQTT_SUCCESS;
}

View File

@ -1,137 +0,0 @@
#ifndef LWMQTT_HELPERS_H
#define LWMQTT_HELPERS_H
#include "lwmqtt.h"
/**
* Reads bits from a byte.
*
* @param byte - The byte to read from.
* @param pos - The position of the first bit.
* @param num - The number of bits to read.
* @return The read bits as a byte.
*/
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num);
/**
* Write bits to a byte.
*
* @param byte - The byte to write bits to.
* @param value - The bits to write as a byte.
* @param pos - The position of the first bit.
* @param num - The number of bits to write.
*/
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num);
/**
* Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param data - Pointer to beginning of data.
* @param len - The amount of data to read.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len);
/**
* Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param data - Pointer to the to be written data.
* @param len - The amount of data to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len);
/**
* Reads two byte number from the specified buffer. The pointer is incremented by two.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param num - The read number.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num);
/**
* Writes a two byte number to the specified buffer. The pointer is incremented by two.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param num - The number to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num);
/**
* Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param str - The object into which the data is to be read.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str);
/**
* Writes a string to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param str - The string to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str);
/**
* Reads one byte from the buffer. The pointer is incremented by one.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param byte - The read byte.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte);
/**
* Writes one byte to the specified buffer. The pointer is incremented by one.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param byte - The byte to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte);
/**
* Returns the amount of bytes required by the variable number.
*
* @param varnum - The number to check.
* @param len - The required length;
* @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len);
/**
* Reads a variable number from the specified buffer. The pointer is incremented by the bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param varnum - The read varnum.
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum);
/**
* Writes a variable number to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param varnum - The number to write.
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum);
#endif

View File

@ -1,381 +0,0 @@
#ifndef LWMQTT_H
#define LWMQTT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* The error type used by all exposed APIs.
*
* If a function returns an error that operates on a connected client (e.g publish, keep_alive, etc.) the caller should
* switch into a disconnected state, close and cleanup the current connection and start over by creating a new
* connection.
*/
typedef enum {
LWMQTT_SUCCESS = 0,
LWMQTT_BUFFER_TOO_SHORT = -1,
LWMQTT_VARNUM_OVERFLOW = -2,
LWMQTT_NETWORK_FAILED_CONNECT = -3,
LWMQTT_NETWORK_TIMEOUT = -4,
LWMQTT_NETWORK_FAILED_READ = -5,
LWMQTT_NETWORK_FAILED_WRITE = -6,
LWMQTT_REMAINING_LENGTH_OVERFLOW = -7,
LWMQTT_REMAINING_LENGTH_MISMATCH = -8,
LWMQTT_MISSING_OR_WRONG_PACKET = -9,
LWMQTT_CONNECTION_DENIED = -10,
LWMQTT_FAILED_SUBSCRIPTION = -11,
LWMQTT_SUBACK_ARRAY_OVERFLOW = -12,
LWMQTT_PONG_TIMEOUT = -13,
} lwmqtt_err_t;
/**
* A common string object.
*/
typedef struct {
uint16_t len;
char *data;
} lwmqtt_string_t;
/**
* The initializer for string objects.
*/
#define lwmqtt_default_string \
{ 0, NULL }
/**
* Returns a string object for the passed C string.
*
* @param str - The C string.
* @return A string object.
*/
lwmqtt_string_t lwmqtt_string(const char *str);
/**
* Compares a string object to a C string.
*
* @param a - The string object to compare.
* @param b - The C string to compare.
* @return Similarity e.g. strcmp().
*/
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b);
/**
* The available QOS levels.
*/
typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t;
/**
* The message object used to publish and receive messages.
*/
typedef struct {
lwmqtt_qos_t qos;
bool retained;
uint8_t *payload;
size_t payload_len;
} lwmqtt_message_t;
/**
* The initializer for message objects.
*/
#define lwmqtt_default_message \
{ LWMQTT_QOS0, false, NULL, 0 }
/**
* Forward declaration of the client object.
*/
typedef struct lwmqtt_client_t lwmqtt_client_t;
/**
* The callback used to read from a network object.
*
* The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified
* timeout and wait for more incoming data.
*
* @param ref - A custom reference.
* @param buf - The buffer.
* @param len - The length of the buffer.
* @param read - Variable that must be set with the amount of read bytes.
* @param timeout - The timeout in milliseconds for the operation.
*/
typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout);
/**
* The callback used to write to a network object.
*
* The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the
* specified timeout to write the specified data to the network.
*
* @param ref - A custom reference.
* @param buf - The buffer.
* @param len - The length of the buffer.
* @param sent - Variable that must be set with the amount of written bytes.
* @param timeout - The timeout in milliseconds for the operation.
*/
typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout);
/**
* The callback used to set a timer.
*
* @param ref - A custom reference.
* @param timeout - The amount of milliseconds until the deadline.
*/
typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout);
/**
* The callback used to get a timers value.
*
* @param - A custom reference.
* @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached.
*/
typedef int32_t (*lwmqtt_timer_get_t)(void *ref);
/**
* The callback used to forward incoming messages.
*
* Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However,
* it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or
* lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not
* recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The
* callback should place the received messages in a queue and dispatch them after the caller has returned.
*/
typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg);
/**
* The client object.
*/
struct lwmqtt_client_t {
uint16_t last_packet_id;
uint32_t keep_alive_interval;
bool pong_pending;
size_t write_buf_size, read_buf_size;
uint8_t *write_buf, *read_buf;
lwmqtt_callback_t callback;
void *callback_ref;
void *network;
lwmqtt_network_read_t network_read;
lwmqtt_network_write_t network_write;
void *keep_alive_timer;
void *command_timer;
lwmqtt_timer_set_t timer_set;
lwmqtt_timer_get_t timer_get;
};
/**
* Will initialize the specified client object.
*
* @param client - The client object.
* @param write_buf - The write buffer.
* @param write_buf_size - The write buffer size.
* @param read_buf - The read buffer.
* @param read_buf_size - The read buffer size.
*/
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
size_t read_buf_size);
/**
* Will set the network reference and callbacks for this client object.
*
* @param client - The client object.
* @param ref - The reference to the network object.
* @param read - The read callback.
* @param write - The write callback.
*/
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write);
/**
* Will set the timer references and callbacks for this client object.
*
* @param client - The client object.
* @param keep_alive_timer - The reference to the keep alive timer.
* @param command_timer - The reference to the command timer.
* @param set - The set callback.
* @param get - The get callback.
*/
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
lwmqtt_timer_get_t get);
/**
* Will set the callback used to receive incoming messages.
*
* @param client - The client object.
* @param ref - A custom reference that will passed to the callback.
* @param cb - The callback to be called.
*/
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb);
/**
* The object defining the last will of a client.
*/
typedef struct {
lwmqtt_string_t topic;
lwmqtt_qos_t qos;
bool retained;
lwmqtt_string_t payload;
} lwmqtt_will_t;
/**
* The default initializer for the will object.
*/
#define lwmqtt_default_will \
{ lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string }
/**
* The object containing the connection options for a client.
*/
typedef struct {
lwmqtt_string_t client_id;
uint16_t keep_alive;
bool clean_session;
lwmqtt_string_t username;
lwmqtt_string_t password;
} lwmqtt_options_t;
/**
* The default initializer for the options object.
*/
#define lwmqtt_default_options \
{ lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string }
/**
* The available return codes transported by the connack packet.
*/
typedef enum {
LWMQTT_CONNECTION_ACCEPTED = 0,
LWMQTT_UNACCEPTABLE_PROTOCOL = 1,
LWMQTT_IDENTIFIER_REJECTED = 2,
LWMQTT_SERVER_UNAVAILABLE = 3,
LWMQTT_BAD_USERNAME_OR_PASSWORD = 4,
LWMQTT_NOT_AUTHORIZED = 5,
LWMQTT_UNKNOWN_RETURN_CODE = 6
} lwmqtt_return_code_t;
/**
* Will send a connect packet and wait for a connack response and set the return code.
*
* The network object must already be connected to the server. An error is returned if the broker rejects the
* connection.
*
* @param client - The client object.
* @param options - The options object.
* @param will - The will object.
* @param return_code - The variable that will receive the return code.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
lwmqtt_return_code_t *return_code, uint32_t timeout);
/**
* Will send a publish packet and wait for all acks to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic - The topic.
* @param message - The message.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout);
/**
* Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param count - The number of topic filters and QOS levels.
* @param topic_filter - The list of topic filters.
* @param qos - The list of QOS levels.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
uint32_t timeout);
/**
* Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic_filter - The topic filter.
* @param qos - The QOS level.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
uint32_t timeout);
/**
* Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param count - The number of topic filters.
* @param topic_filter - The topic filter.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout);
/**
* Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic_filter - The topic filter.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout);
/**
* Will send a disconnect packet and finish the client.
*
* @param client - The client object.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout);
/**
* Will yield control to the client and receive incoming packets from the network.
*
* Single-threaded applications may peek on the network and assess if data is available to read before calling yield and
* potentially block until the timeout is reached. Multi-threaded applications may select on the socket and block until
* data is available and then yield to the client if data is available. All applications may specify the amount of bytes
* available to read in order to constrain the yield to only receive packets that are already in-flight.
*
* If no availability info is given the yield will return after one packet has been successfully read or the deadline
* has been reached but no single bytes has been received.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param available - The available bytes to read.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout);
/**
* Will yield control to the client to keep the connection alive.
*
* This functions must be called at a rate slightly lower than 25% of the configured keep alive. If keep alive is zero,
* the function must not be called at all.
*
* @param client - The client object.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout);
#endif // LWMQTT_H

View File

@ -1,742 +0,0 @@
#include "packet.h"
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) {
// set default packet type
*packet_type = LWMQTT_NO_PACKET;
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// prepare header
uint8_t header;
// read header
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// get packet type
*packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4);
// check if packet type is correct and can be received
switch (*packet_type) {
case LWMQTT_CONNACK_PACKET:
case LWMQTT_PUBLISH_PACKET:
case LWMQTT_PUBACK_PACKET:
case LWMQTT_PUBREC_PACKET:
case LWMQTT_PUBREL_PACKET:
case LWMQTT_PUBCOMP_PACKET:
case LWMQTT_SUBACK_PACKET:
case LWMQTT_UNSUBACK_PACKET:
case LWMQTT_PINGRESP_PACKET:
return LWMQTT_SUCCESS;
default:
*packet_type = LWMQTT_NO_PACKET;
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
}
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) {
// prepare pointer
uint8_t *ptr = buf;
// attempt to decode remaining length
lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
*rem_len = 0;
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
} else if (err != LWMQTT_SUCCESS) {
*rem_len = 0;
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
lwmqtt_will_t *will) {
// prepare pointers
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// fixed header is 10
uint32_t rem_len = 10;
// add client id to remaining length
rem_len += options.client_id.len + 2;
// add will if present to remaining length
if (will != NULL) {
rem_len += will->topic.len + 2 + will->payload.len + 2;
}
// add username if present to remaining length
if (options.username.len > 0) {
rem_len += options.username.len + 2;
// add password if present to remaining length
if (options.password.len > 0) {
rem_len += options.password.len + 2;
}
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write version string
err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT"));
if (err != LWMQTT_SUCCESS) {
return err;
}
// write version number
err = lwmqtt_write_byte(&buf_ptr, buf_end, 4);
if (err != LWMQTT_SUCCESS) {
return err;
}
// prepare flags
uint8_t flags = 0;
// set clean session
lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1);
// set will flags if present
if (will != NULL) {
lwmqtt_write_bits(&flags, 1, 2, 1);
lwmqtt_write_bits(&flags, will->qos, 3, 2);
lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1);
}
// set username flag if present
if (options.username.len > 0) {
lwmqtt_write_bits(&flags, 1, 7, 1);
// set password flag if present
if (options.password.len > 0) {
lwmqtt_write_bits(&flags, 1, 6, 1);
}
}
// write flags
err = lwmqtt_write_byte(&buf_ptr, buf_end, flags);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write keep alive
err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write client id
err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write will if present
if (will != NULL) {
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write payload length
err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write payload
err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write username if present
if (options.username.len > 0) {
err = lwmqtt_write_string(&buf_ptr, buf_end, options.username);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write password if present
if (options.username.len > 0 && options.password.len > 0) {
err = lwmqtt_write_string(&buf_ptr, buf_end, options.password);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set written length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
lwmqtt_return_code_t *return_code) {
// prepare pointers
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length
if (rem_len != 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read flags
uint8_t flags;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read return code
uint8_t raw_return_code;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code);
if (err != LWMQTT_SUCCESS) {
return err;
}
// get session present
*session_present = lwmqtt_read_bits(flags, 7, 1) == 1;
// get return code
switch (raw_return_code) {
case 0:
*return_code = LWMQTT_CONNECTION_ACCEPTED;
break;
case 1:
*return_code = LWMQTT_UNACCEPTABLE_PROTOCOL;
break;
case 2:
*return_code = LWMQTT_IDENTIFIER_REJECTED;
break;
case 3:
*return_code = LWMQTT_SERVER_UNAVAILABLE;
break;
case 4:
*return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD;
break;
case 5:
*return_code = LWMQTT_NOT_AUTHORIZED;
break;
default:
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// write header
uint8_t header = 0;
lwmqtt_write_bits(&header, packet_type, 4, 4);
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
uint16_t *packet_id) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header = 0;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != packet_type) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// get dup
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length
if (rem_len != 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read packet id
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
uint16_t packet_id) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, packet_type, 4, 4);
// set dup
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
// set qos
lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2);
// write header
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set written length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
lwmqtt_message_t *msg) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// get dup
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
// get retained
msg->retained = lwmqtt_read_bits(header, 0, 1) == 1;
// get qos
switch (lwmqtt_read_bits(header, 1, 2)) {
case 0:
msg->qos = LWMQTT_QOS0;
break;
case 1:
msg->qos = LWMQTT_QOS1;
break;
case 2:
msg->qos = LWMQTT_QOS2;
break;
default:
msg->qos = LWMQTT_QOS0;
break;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length (topic length)
if (rem_len < 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// check buffer capacity
if ((uint32_t)(buf_end - buf_ptr) < rem_len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// reset buf end
buf_end = buf_ptr + rem_len;
// read topic
err = lwmqtt_read_string(&buf_ptr, buf_end, topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read packet id if qos is at least 1
if (msg->qos > 0) {
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
} else {
*packet_id = 0;
}
// set payload length
msg->payload_len = buf_end - buf_ptr;
// read payload
err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
lwmqtt_string_t topic, lwmqtt_message_t msg) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len;
if (msg.qos > 0) {
rem_len += 2;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4);
// set dup
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
// set qos
lwmqtt_write_bits(&header, msg.qos, 1, 2);
// set retained
lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id if qos is at least 1
if (msg.qos > 0) {
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write payload
err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2;
for (int i = 0; i < count; i++) {
rem_len += 2 + topic_filters[i].len + 1;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4);
// set qos
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write all subscriptions
for (int i = 0; i < count; i++) {
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write qos level
err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
lwmqtt_qos_t *granted_qos_levels) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length (packet id + min. one suback code)
if (rem_len < 3) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read packet id
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read all suback codes
for (*count = 0; *count < (int)rem_len - 2; (*count)++) {
// check max count
if (*count > max_count) {
return LWMQTT_SUBACK_ARRAY_OVERFLOW;
}
// read qos level
uint8_t raw_qos_level;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set qos level
switch (raw_qos_level) {
case 0:
granted_qos_levels[*count] = LWMQTT_QOS0;
break;
case 1:
granted_qos_levels[*count] = LWMQTT_QOS1;
break;
case 2:
granted_qos_levels[*count] = LWMQTT_QOS2;
break;
default:
granted_qos_levels[*count] = LWMQTT_QOS_FAILURE;
break;
}
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2;
for (int i = 0; i < count; i++) {
rem_len += 2 + topic_filters[i].len;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4);
// set qos
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write topics
for (int i = 0; i < count; i++) {
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}

View File

@ -1,185 +0,0 @@
#ifndef LWMQTT_PACKET_H
#define LWMQTT_PACKET_H
#include "helpers.h"
/**
* The available packet types.
*/
typedef enum {
LWMQTT_NO_PACKET = 0,
LWMQTT_CONNECT_PACKET = 1,
LWMQTT_CONNACK_PACKET,
LWMQTT_PUBLISH_PACKET,
LWMQTT_PUBACK_PACKET,
LWMQTT_PUBREC_PACKET,
LWMQTT_PUBREL_PACKET,
LWMQTT_PUBCOMP_PACKET,
LWMQTT_SUBSCRIBE_PACKET,
LWMQTT_SUBACK_PACKET,
LWMQTT_UNSUBSCRIBE_PACKET,
LWMQTT_UNSUBACK_PACKET,
LWMQTT_PINGREQ_PACKET,
LWMQTT_PINGRESP_PACKET,
LWMQTT_DISCONNECT_PACKET
} lwmqtt_packet_type_t;
/**
* Will detect the packet type from the at least one byte long buffer.
*
* @param buf - The buffer from which the packet type will be detected.
* @param buf_len - The length of the specified buffer.
* @param packet_type - The packet type.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type);
/**
* Will detect the remaining length form the at least on byte long buffer.
*
* It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the
* network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW.
*
* @param buf - The buffer from which the remaining length will be detected.
* @param buf_len - The length of the specified buffer.
* @param rem_len - The detected remaining length.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len);
/**
* Encodes a connect packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param options - The options to be used to build the connect packet.
* @param will - The last will and testament.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
lwmqtt_will_t *will);
/**
* Decodes a connack packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param session_present - The session present flag.
* @param return_code - The return code.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
lwmqtt_return_code_t *return_code);
/**
* Encodes a zero (disconnect, pingreq) packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_type - The packets type.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type);
/**
* Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param packet_type - The packet type.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
uint16_t *packet_id);
/**
* Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_type - The packets type.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
uint16_t packet_id);
/**
* Decodes a publish packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @param topic - The topic.
* @parma msg - The message.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
lwmqtt_message_t *msg);
/**
* Encodes a publish packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @param topic - The topic.
* @param msg - The message.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
lwmqtt_string_t topic, lwmqtt_message_t msg);
/**
* Encodes a subscribe packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_id - The packet id.
* @param count - The number of members in the topic_filters and qos_levels array.
* @param topic_filters - The array of topic filter.
* @param qos_levels - The array of requested QoS levels.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels);
/**
* Decodes a suback packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param packet_id - The packet id.
* @param max_count - The maximum number of members allowed in the granted_qos_levels array.
* @param count - The number of members in the granted_qos_levels array.
* @param granted_qos_levels - The granted QoS levels.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
lwmqtt_qos_t *granted_qos_levels);
/**
* Encodes the supplied unsubscribe data into the supplied buffer, ready for sending
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_id - The packet id.
* @param count - The number of members in the topic_filters array.
* @param topic_filters - The array of topic filters.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters);
#endif // LWMQTT_PACKET_H

View File

@ -1,38 +0,0 @@
#include <string.h>
#include "lwmqtt.h"
lwmqtt_string_t lwmqtt_string(const char *str) {
// check for null
if (str == NULL) {
return (lwmqtt_string_t){0, NULL};
}
// get length
uint16_t len = (uint16_t)strlen(str);
// check zero length
if (len == 0) {
return (lwmqtt_string_t){0, NULL};
}
return (lwmqtt_string_t){len, (char *)str};
}
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) {
// get string of b
lwmqtt_string_t b_str = lwmqtt_string(b);
// return if both are zero length
if (a.len == 0 && b_str.len == 0) {
return 0;
}
// return if lengths are different
if (a.len != b_str.len) {
return -1;
}
// compare memory of same length
return strncmp(a.data, b_str.data, a.len);
}

View File

@ -89,6 +89,10 @@ build_flags = ${esp82xx_defaults.build_flags}
platform = https://github.com/platformio/platform-espressif8266.git#feature/stage
build_flags = ${esp82xx_defaults.build_flags}
-Wl,-Teagle.flash.1m.ld
; nonos-sdk 22x
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x
; nonos-sdk-pre-v3
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
; lwIP 1.4 (Default)
; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; lwIP 2 - Low Memory

View File

@ -1,5 +1,12 @@
/* 6.4.1.21 20190309
/* 6.4.2.22 20190315
* Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474)
*
* 6.4.1.21 20190309
* Fix exception on GUI Configure Logging and Configure Other (#5424)
* Add support for sensor SCD30 (#5434)
* Add support for commands in sensor drivers
* Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431)
* Add button control when no relay configured (#4682)
*
* 6.4.1.20 20190304
* Changed webserver content handling from single String to small Chunks increasing RAM

View File

@ -568,9 +568,13 @@ const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m}
const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "%s{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798)
#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) || defined(USE_SCD30)
const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#endif // USE_WEBSERVER
#endif // USE_MHZ19
#if defined(USE_SCD30)
const char HTTP_SNS_CO2EAVG[] PROGMEM = "%s{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#endif // USE_SCD30
const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU;
const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION;

View File

@ -55,20 +55,20 @@
#define D_AIR_QUALITY "איכות אוויר"
#define D_AP "AP" // Access Point
#define D_AS "as"
#define D_AUTO "AUTO"
#define D_AUTO "אוטומטי"
#define D_BLINK "מהבהב"
#define D_BLINKOFF "כיבוי היבהוב"
#define D_BOOT_COUNT "מונה הפעלה מחדש"
#define D_BRIGHTLIGHT "בהירות"
#define D_BSSID "BSSId"
#define D_BUTTON "לחצן"
#define D_BY "by" // Written by me
#define D_BY "ע" // Written by me
#define D_BYTES "בייט"
#define D_CELSIUS "צלזיוס"
#define D_CHANNEL "ערוץ"
#define D_CO2 "Carbon dioxide"
#define D_CODE "קוד" // Button code
#define D_COLDLIGHT "קור"
#define D_COLDLIGHT "אור קר"
#define D_COMMAND "פקודה"
#define D_CONNECTED "מחובר"
#define D_COUNT "סופר"
@ -148,7 +148,7 @@
#define D_STOP "עצירה"
#define D_SUBNET_MASK "רשת מסכת משנה"
#define D_SUBSCRIBE_TO "הרשם ל"
#define D_UNSUBSCRIBE_FROM "Unsubscribe from"
#define D_UNSUBSCRIBE_FROM "בטל רישום"
#define D_SUCCESSFUL "הצליח"
#define D_SUNRISE "זריחה"
#define D_SUNSET "שקיעה"
@ -176,33 +176,33 @@
#define D_UV_POWER "UV Power"
#define D_VERSION "גרסה"
#define D_VOLTAGE "מתח"
#define D_WEIGHT "Weight"
#define D_WEIGHT "משקל"
#define D_WARMLIGHT "חום"
#define D_WEB_SERVER "Web שרת"
// sonoff.ino
#define D_WARNING_MINIMAL_VERSION "WARNING This version does not support persistent settings"
#define D_WARNING_MINIMAL_VERSION "אזהרה גרסה זו אינה תומכת בהגדרות קבועות"
#define D_LEVEL_10 "level 1-0"
#define D_LEVEL_01 "level 0-1"
#define D_SERIAL_LOGGING_DISABLED "Serial logging disabled"
#define D_SYSLOG_LOGGING_REENABLED "Syslog logging re-enabled"
#define D_SERIAL_LOGGING_DISABLED "רישום טורי מושבת"
#define D_SYSLOG_LOGGING_REENABLED "הופעל מחדש Syslog רישום"
#define D_SET_BAUDRATE_TO "Set Baudrate to"
#define D_RECEIVED_TOPIC "Received Topic"
#define D_DATA_SIZE "Data Size"
#define D_SET_BAUDRATE_TO "הגדר קצב שידור ל"
#define D_RECEIVED_TOPIC "Topic התקבל"
#define D_DATA_SIZE "גודל נתונים"
#define D_ANALOG_INPUT "אנלוגי"
// support.ino
#define D_OSWATCH "osWatch"
#define D_BLOCKED_LOOP "Blocked Loop"
#define D_WPS_FAILED_WITH_STATUS "WPSconfig FAILED with status"
#define D_ACTIVE_FOR_3_MINUTES "active for 3 minutes"
#define D_FAILED_TO_START "failed to start"
#define D_PATCH_ISSUE_2186 "Patch issue 2186"
#define D_CONNECTING_TO_AP "Connecting to AP"
#define D_IN_MODE "in mode"
#define D_CONNECT_FAILED_NO_IP_ADDRESS "Connect failed as no IP address received"
#define D_CONNECT_FAILED_AP_NOT_REACHED "Connect failed as AP cannot be reached"
#define D_BLOCKED_LOOP "לולאות חסומות"
#define D_WPS_FAILED_WITH_STATUS "נכשל עם הסטטוס WPSconfig"
#define D_ACTIVE_FOR_3_MINUTES "פעיל במשך 3 דקות"
#define D_FAILED_TO_START "נכשל בנסיון להתחיל"
#define D_PATCH_ISSUE_2186 "בעית תיקון 2186"
#define D_CONNECTING_TO_AP "AP -מתחבר ל"
#define D_IN_MODE "במצב"
#define D_CONNECT_FAILED_NO_IP_ADDRESS "IP החיבור נכשל מכיוון שלא התקבלה כתובת"
#define D_CONNECT_FAILED_AP_NOT_REACHED "זמין AP החיבור נכשל כיוון שאין"
#define D_CONNECT_FAILED_WRONG_PASSWORD "Connect failed with AP incorrect password"
#define D_CONNECT_FAILED_AP_TIMEOUT "Connect failed with AP timeout"
#define D_ATTEMPTING_CONNECTION "Attempting connection..."
@ -213,19 +213,19 @@
#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host not found"
// settings.ino
#define D_SAVED_TO_FLASH_AT "Saved to flash at"
#define D_LOADED_FROM_FLASH_AT "Loaded from flash at"
#define D_SAVED_TO_FLASH_AT "נשמר לפלאש ב"
#define D_LOADED_FROM_FLASH_AT "נטען מהפלאש מ"
#define D_USE_DEFAULTS "השתמש בהגדרות ברירת המחדל"
#define D_ERASED_SECTOR "סקטור מחוק"
// xdrv_02_webserver.ino
#define D_NOSCRIPT "JavaScript - כדי להשתמש ב קושחה אסמוטה אנא הפעל"
#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "קושחה מינימלית - בבקשה אנא שדרג"
#define D_WEBSERVER_ACTIVE_ON "Web server active on"
#define D_WITH_IP_ADDRESS "with IP address"
#define D_WEBSERVER_STOPPED "Web server stopped"
#define D_FILE_NOT_FOUND "File Not Found"
#define D_REDIRECTED "Redirected to captive portal"
#define D_WEBSERVER_ACTIVE_ON "שרת ווב פעיל"
#define D_WITH_IP_ADDRESS "IP עם כתובת"
#define D_WEBSERVER_STOPPED "שרת ווב הופסק"
#define D_FILE_NOT_FOUND "קובץ לא נמצא"
#define D_REDIRECTED "הופנה מחדש לפורטל"
#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager set AccessPoint and keep Station"
#define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager set AccessPoint"
#define D_TRYING_TO_CONNECT "מנסה לחבר את ההתקן לרשת"
@ -254,7 +254,7 @@
#define D_MODULE_PARAMETERS "מודול פרמטרים"
#define D_MODULE_TYPE "סוג מודול"
#define D_PULLUP_ENABLE "No Button/Switch pull-up"
#define D_PULLUP_ENABLE "pull-up אין לחצן/מתג"
#define D_GPIO " רגל "
#define D_SERIAL_IN "כניסת סריאל"
#define D_SERIAL_OUT "יציאת סריאל"
@ -299,13 +299,13 @@
#define D_SINGLE_DEVICE "התקן בודד"
#define D_MULTI_DEVICE "התקנים"
#define D_CONFIGURE_TEMPLATE "Configure Template"
#define D_TEMPLATE_PARAMETERS "Template parameters"
#define D_TEMPLATE_NAME "Name"
#define D_BASE_TYPE "Based on"
#define D_TEMPLATE_FLAGS "Options"
#define D_ALLOW_ADC0 "ADC0 input"
#define D_ALLOW_PULLUP "User pull-up selection"
#define D_CONFIGURE_TEMPLATE "הגדר תבנית"
#define D_TEMPLATE_PARAMETERS "פרמטרים של תבנית"
#define D_TEMPLATE_NAME "שם"
#define D_BASE_TYPE "מבוסס על"
#define D_TEMPLATE_FLAGS "אפשריות"
#define D_ALLOW_ADC0 "ADC0 כניסת"
#define D_ALLOW_PULLUP "pull-up בחירת משתמש עבור"
#define D_SAVE_CONFIGURATION "שמירת הגדרות"
#define D_CONFIGURATION_SAVED "הגדרות נשמרו"
@ -357,12 +357,12 @@
#define D_NEED_USER_AND_PASSWORD "Need user=<username>&password=<password>"
// xdrv_01_mqtt.ino
#define D_FINGERPRINT "Verify TLS fingerprint..."
#define D_TLS_CONNECT_FAILED_TO "TLS Connect failed to"
#define D_RETRY_IN "Retry in"
#define D_VERIFIED "Verified using Fingerprint"
#define D_INSECURE "Insecure connection due to invalid Fingerprint"
#define D_CONNECT_FAILED_TO "Connect failed to"
#define D_FINGERPRINT "...TLS אמת טביעת אצבע של"
#define D_TLS_CONNECT_FAILED_TO "נכשל TLS חיבור"
#define D_RETRY_IN "נסה שוב תוך"
#define D_VERIFIED "מאומת באמצעות טביעת אצבע"
#define D_INSECURE "חיבור לא מאובטח עקב טביעת אצבע לא חוקית"
#define D_CONNECT_FAILED_TO "חיבור נכשל ל"
// xplg_wemohue.ino
#define D_MULTICAST_DISABLED "Multicast disabled"
@ -441,16 +441,16 @@
#define D_ENERGY_TOTAL "צריכה כללית"
// xsns_05_ds18b20.ino
#define D_SENSOR_BUSY "Sensor busy"
#define D_SENSOR_CRC_ERROR "Sensor CRC error"
#define D_SENSORS_FOUND "Sensors found"
#define D_SENSOR_BUSY "שרת עסוק"
#define D_SENSOR_CRC_ERROR "בחיישן CRC שגיאת"
#define D_SENSORS_FOUND "חיישנים לא נמצאו"
// xsns_06_dht.ino
#define D_TIMEOUT_WAITING_FOR "Timeout waiting for"
#define D_START_SIGNAL_LOW "start signal low"
#define D_START_SIGNAL_HIGH "start signal high"
#define D_PULSE "pulse"
#define D_CHECKSUM_FAILURE "Checksum failure"
#define D_TIMEOUT_WAITING_FOR "הזמן הקצוב להמתנה"
#define D_START_SIGNAL_LOW "להתחיל אות נמוך"
#define D_START_SIGNAL_HIGH "להתחיל אות גבוה"
#define D_PULSE "פעימה"
#define D_CHECKSUM_FAILURE "בדיקת כשל נכשלה"
// xsns_07_sht1x.ino
#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor did not ACK command"
@ -470,17 +470,17 @@
#define D_GZ_AXIS "Gyro Z-Axis"
// xsns_34_hx711.ino
#define D_HX_CAL_REMOVE "Remove weigth"
#define D_HX_CAL_REFERENCE "Load reference weigth"
#define D_HX_CAL_DONE "Calibrated"
#define D_HX_CAL_FAIL "Calibration failed"
#define D_RESET_HX711 "Reset Scale"
#define D_CONFIGURE_HX711 "Configure Scale"
#define D_HX711_PARAMETERS "Scale parameters"
#define D_ITEM_WEIGHT "Item weight"
#define D_REFERENCE_WEIGHT "Reference weigth"
#define D_CALIBRATE "Calibrate"
#define D_CALIBRATION "Calibration"
#define D_HX_CAL_REMOVE "הסר משקל"
#define D_HX_CAL_REFERENCE "טען משקל התייחסות"
#define D_HX_CAL_DONE "מכויל"
#define D_HX_CAL_FAIL "כיול נכשל"
#define D_RESET_HX711 "אפס את קנה המידה"
#define D_CONFIGURE_HX711 "הגדר קנה מידה"
#define D_HX711_PARAMETERS "פרמטרים של קנה מידה"
#define D_ITEM_WEIGHT "משקל פריט"
#define D_REFERENCE_WEIGHT "משקל הפניה"
#define D_CALIBRATE "כייל"
#define D_CALIBRATION "כיול"
//xsns_35_tx20.ino
#define D_TX20_WIND_DIRECTION "Wind Direction"
@ -494,7 +494,7 @@
// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box
#define D_SENSOR_NONE "None"
#define D_SENSOR_USER "User"
#define D_SENSOR_USER "משתמש"
#define D_SENSOR_DHT11 "DHT11"
#define D_SENSOR_AM2301 "AM2301"
#define D_SENSOR_SI7021 "SI7021"
@ -502,7 +502,7 @@
#define D_SENSOR_I2C_SCL "I2C SCL"
#define D_SENSOR_I2C_SDA "I2C SDA"
#define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player"
#define D_SENSOR_DFR562 "נגן מוזיקה"
#define D_SENSOR_IRSEND "IRsend"
#define D_SENSOR_SWITCH "מתג" // Suffix "1"
#define D_SENSOR_BUTTON "לחצן" // Suffix "1"

View File

@ -230,16 +230,6 @@
// -- OTA -----------------------------------------
//#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+13k code)
/*-------------------------------------------------------------------------------------------*\
* Select ONE of possible MQTT library types below
\*-------------------------------------------------------------------------------------------*/
// Default MQTT driver for both non-TLS and TLS connections. Latest library version (20181016) does not block network if MQTT server is unavailable.
#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library
// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support
//#define MQTT_LIBRARY_TYPE MQTT_TASMOTAMQTT // Use TasmotaMqtt library (+4k4 (core 2.3.0), +14k4 (core 2.4.2 lwip2) code, +4k mem) - non-TLS only
// Alternative MQTT driver does not block network when MQTT server is unavailable. TLS should work but needs to be tested.
//#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Use arduino-mqtt (lwmqtt) library (+3k3 code, +2k mem)
// -- MQTT ----------------------------------------
#define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on)
@ -249,7 +239,7 @@
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // Domoticz Output Topic
// -- MQTT - Home Assistant Discovery -------------
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+2k code)
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code)
#define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix
// -- MQTT - TLS ----------------------------------
@ -331,6 +321,7 @@
// #define USE_RTC_ADDR 0x68 // Default I2C address 0x68
// #define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
// #define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
// #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0

View File

@ -200,8 +200,8 @@ struct SYSCFG {
uint8_t seriallog_level; // 09E
uint8_t sta_config; // 09F
uint8_t sta_active; // 0A0
char sta_ssid[2][33]; // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 4/5
char sta_pwd[2][65]; // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 4/5
char sta_ssid[2][33]; // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 5
char sta_pwd[2][65]; // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 5
char hostname[33]; // 165
char syslog_host[33]; // 186
uint8_t rule_stop; // 1A7
@ -213,12 +213,12 @@ struct SYSCFG {
uint8_t free_1D5[20]; // 1D5 Free since 5.12.0e
char mqtt_host[33]; // 1E9
uint16_t mqtt_port; // 20A
char mqtt_client[33]; // 20C
char mqtt_user[33]; // 22D
char mqtt_pwd[33]; // 24E
char mqtt_topic[33]; // 26F
char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6
uint16_t mqtt_port; // 20A - Keep together
char mqtt_client[33]; // 20C - Keep together
char mqtt_user[33]; // 22D - Keep together
char mqtt_pwd[33]; // 24E - Keep together
char mqtt_topic[33]; // 26F - Keep together with above items as being copied as one chunck with reset 6
char button_topic[33]; // 290
char mqtt_grptopic[33]; // 2B1
uint8_t display_model; // 2D2

View File

@ -252,7 +252,7 @@ enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MA
enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT,
FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND,
FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND,
FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER,
FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA,
FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR,
FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED,

View File

@ -462,7 +462,9 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
uint16_t index;
uint32_t address;
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("MqttDataHandler"));
#endif
strlcpy(topicBuf, topic, sizeof(topicBuf));
for (i = 0; i < data_len; i++) {
@ -527,8 +529,18 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
int command_code = GetCommandCode(command, sizeof(command), type, kTasmotaCommands);
if (-1 == command_code) {
if (!XdrvCommand(grpflg, type, index, dataBuf, data_len, payload, payload16)) {
type = NULL; // Unknown command
// XdrvMailbox.valid = 1;
XdrvMailbox.index = index;
XdrvMailbox.data_len = data_len;
XdrvMailbox.payload16 = payload16;
XdrvMailbox.payload = payload;
XdrvMailbox.grpflg = grpflg;
XdrvMailbox.topic = type;
XdrvMailbox.data = dataBuf;
if (!XdrvCall(FUNC_COMMAND)) {
if (!XsnsCall(FUNC_COMMAND)) {
type = NULL; // Unknown command
}
}
}
else if (CMND_BACKLOG == command_code) {
@ -711,9 +723,9 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
XdrvMailbox.topic = command;
XdrvMailbox.data = dataBuf;
if (CMND_SENSOR == command_code) {
XsnsCall(FUNC_COMMAND);
XsnsCall(FUNC_COMMAND_SENSOR);
} else {
XdrvCall(FUNC_COMMAND);
XdrvCall(FUNC_COMMAND_DRIVER);
}
}
else if ((CMND_SETOPTION == command_code) && (index < 82)) {
@ -1056,7 +1068,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce);
}
else if (CMND_BAUDRATE == command_code) {
if (payload32 > 0) {
if (payload32 > 1200) {
payload32 /= 1200; // Make it a valid baudrate
baudrate = (1 == payload) ? APP_BAUDRATE : payload32 * 1200;
SetSerialBaudrate(baudrate);
@ -1585,7 +1597,9 @@ void ExecuteCommand(char *cmnd, int source)
char *start;
char *token;
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("ExecuteCommand"));
#endif
ShowSource(source);
token = strtok(cmnd, " ");
@ -1668,8 +1682,8 @@ void PublishStatus(uint8_t payload)
}
if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MqttType\":%d,\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"),
Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttLibraryType(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"),
Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE);
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6"));
}
@ -2068,31 +2082,26 @@ void Every250mSeconds(void)
}
if (restart_flag && (backlog_pointer == backlog_index)) {
if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
char storage[sizeof(Settings.sta_ssid) + sizeof(Settings.sta_pwd)];
char storage_mqtt_host[sizeof(Settings.mqtt_host)];
uint16_t storage_mqtt_port;
char storage_mqtt_user[sizeof(Settings.mqtt_user)];
char storage_mqtt_pwd[sizeof(Settings.mqtt_pwd)];
char storage_mqtt_topic[sizeof(Settings.mqtt_topic)];
memcpy(storage, Settings.sta_ssid, sizeof(storage)); // Backup current SSIDs and Passwords
char storage_wifi[sizeof(Settings.sta_ssid) +
sizeof(Settings.sta_pwd)];
char storage_mqtt[sizeof(Settings.mqtt_host) +
sizeof(Settings.mqtt_port) +
sizeof(Settings.mqtt_client) +
sizeof(Settings.mqtt_user) +
sizeof(Settings.mqtt_pwd) +
sizeof(Settings.mqtt_topic)];
memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords
if (216 == restart_flag) {
memcpy(storage_mqtt_host, Settings.mqtt_host, sizeof(Settings.mqtt_host));
storage_mqtt_port = Settings.mqtt_port;
memcpy(storage_mqtt_user, Settings.mqtt_user, sizeof(Settings.mqtt_user));
memcpy(storage_mqtt_pwd, Settings.mqtt_pwd, sizeof(Settings.mqtt_pwd));
memcpy(storage_mqtt_topic, Settings.mqtt_topic, sizeof(Settings.mqtt_topic));
memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password
}
if ((215 == restart_flag) || (216 == restart_flag)) {
SettingsErase(0); // Erase all flash from program end to end of physical flash
}
SettingsDefault();
memcpy(Settings.sta_ssid, storage, sizeof(storage)); // Restore current SSIDs and Passwords
if (216 == restart_flag) { // Restore the mqtt host, port, username and password
memcpy(Settings.mqtt_host, storage_mqtt_host, sizeof(Settings.mqtt_host));
Settings.mqtt_port = storage_mqtt_port;
memcpy(Settings.mqtt_user, storage_mqtt_user, sizeof(Settings.mqtt_user));
memcpy(Settings.mqtt_pwd, storage_mqtt_pwd, sizeof(Settings.mqtt_pwd));
memcpy(Settings.mqtt_topic, storage_mqtt_topic, sizeof(Settings.mqtt_topic));
memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi)); // Restore current SSIDs and Passwords
if (216 == restart_flag) {
memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password
strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default
}
restart_flag = 2;
}

View File

@ -92,7 +92,8 @@ void KNX_CB_Action(message_t const &msg, void *arg);
//#define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code)
//#define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code)
//#define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem)
//#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
//#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code)
#define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code)
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
#ifndef CO2_LOW
@ -157,8 +158,6 @@ void KNX_CB_Action(message_t const &msg, void *arg);
#ifndef USE_SMARTCONFIG
#define USE_SMARTCONFIG // Add support for Wifi SmartConfig as initial wifi configuration tool (+23k code, +0.6k mem)
#endif
#undef MQTT_LIBRARY_TYPE
#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library
#undef USE_ARDUINO_OTA // Disable support for Arduino OTA
//#undef USE_DOMOTICZ // Disable Domoticz
#undef USE_HOME_ASSISTANT // Disable Home Assistant

View File

@ -20,7 +20,7 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
#define VERSION 0x06040115
#define VERSION 0x06040116
#define D_PROGRAMNAME "Sonoff-Tasmota"
#define D_AUTHOR "Theo Arends"

View File

@ -1278,7 +1278,7 @@ void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...)
{
va_list arg;
va_start(arg, formatP);
int len = vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
va_end(arg);
AddLog(loglevel);

View File

@ -95,8 +95,9 @@ void ButtonHandler(void)
uint16_t loops_per_second = 1000 / Settings.button_debounce;
char scmnd[20];
uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present;
for (uint8_t button_index = 0; button_index < maxdev; button_index++) {
// uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present;
// for (uint8_t button_index = 0; button_index < maxdev; button_index++) {
for (uint8_t button_index = 0; button_index < MAX_KEYS; button_index++) {
button = NOT_PRESSED;
button_present = 0;

View File

@ -56,10 +56,10 @@ void GetFeatures(void)
feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino
#endif
#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
feature_drv1 |= 0x00000800; // xdrv_01_mqtt.ino
// feature_drv1 |= 0x00000800; // xdrv_01_mqtt.ino
#endif
#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11
feature_drv1 |= 0x00001000; // xdrv_01_mqtt.ino
// feature_drv1 |= 0x00001000; // xdrv_01_mqtt.ino
#endif
#ifdef MQTT_HOST_DISCOVERY
feature_drv1 |= 0x00002000; // xdrv_01_mqtt.ino
@ -116,7 +116,7 @@ void GetFeatures(void)
feature_drv1 |= 0x40000000; // support.ino
#endif
#if (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT)
feature_drv1 |= 0x80000000; // xdrv_01_mqtt.ino
// feature_drv1 |= 0x80000000; // xdrv_01_mqtt.ino
#endif
/*********************************************************************************************/
@ -383,8 +383,10 @@ void GetFeatures(void)
#endif
#ifdef USE_MAX44009
feature_sns2 |= 0x00200000;
#endif
// feature_sns2 |= 0x00400000;
#endif
#ifdef USE_SCD30
feature_sns2 |= 0x00400000;
#endif
// feature_sns2 |= 0x00800000;
// feature_sns2 |= 0x01000000;
// feature_sns2 |= 0x02000000;

View File

@ -241,14 +241,14 @@ const char HTTP_HEAD_STYLE1[] PROGMEM =
"div,fieldset,input,select{padding:5px;font-size:1em;}"
"fieldset{background-color:#f2f2f2;}" // Also update HTTP_TIMER_STYLE
"p{margin:0.5em 0;}"
"input{width:100%;box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;}"
"input{width:100%%;box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;}"
"input[type=checkbox],input[type=radio]{width:1em;margin-right:6px;vertical-align:-1px;}"
"select{width:100%;}"
"textarea{resize:none;width:98%;height:318px;padding:5px;overflow:auto;}"
"select{width:100%%;}"
"textarea{resize:none;width:98%%;height:318px;padding:5px;overflow:auto;}"
"body{text-align:center;font-family:verdana;}"
"td{padding:0px;}";
const char HTTP_HEAD_STYLE2[] PROGMEM =
"button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;-webkit-transition-duration:0.4s;transition-duration:0.4s;cursor:pointer;}"
"button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%%;-webkit-transition-duration:0.4s;transition-duration:0.4s;cursor:pointer;}"
"button:hover{background-color:#0e70a4;}"
".bred{background-color:#d43535;}"
".bred:hover{background-color:#931f1f;}"
@ -272,7 +272,7 @@ const char HTTP_HEAD_STYLE3[] PROGMEM =
#else
"<h3>%s " D_MODULE "</h3>"
#endif
"<h2>%s</h2>%s</div>";
"<h2>%s</h2>";
const char HTTP_MSG_SLIDER1[] PROGMEM =
"<div><span class='p'>" D_COLDLIGHT "</span><span class='q'>" D_WARMLIGHT "</span></div>"
@ -283,37 +283,6 @@ const char HTTP_MSG_SLIDER2[] PROGMEM =
const char HTTP_MSG_RSTRT[] PROGMEM =
"<br/><div style='text-align:center;'>" D_DEVICE_WILL_RESTART "</div><br/>";
const char HTTP_BTN_CONF[] PROGMEM =
"<br/>"
"<form action='cn' method='get'><button>" D_CONFIGURATION "</button></form>";
const char HTTP_BTN_MENU1[] PROGMEM =
#ifndef FIRMWARE_MINIMAL
"<p><form action='in' method='get'><button>" D_INFORMATION "</button></form></p>"
#endif
"<form action='up' method='get'><button>" D_FIRMWARE_UPGRADE "</button></form>"
"<p><form action='cs' method='get'><button>" D_CONSOLE "</button></form></p>";
const char HTTP_BTN_RSTRT[] PROGMEM =
"<form action='.' method='get' onsubmit='return confirm(\"" D_CONFIRM_RESTART "\");'><button name='rstrt' class='button bred'>" D_RESTART "</button></form>";
const char HTTP_BTN_MENU_MODULE[] PROGMEM =
"<p><form action='md' method='get'><button>" D_CONFIGURE_MODULE "</button></form></p>"
"<p><form action='wi' method='get'><button>" D_CONFIGURE_WIFI "</button></form></p>";
const char HTTP_BTN_MENU4[] PROGMEM =
"<p><form action='lg' method='get'><button>" D_CONFIGURE_LOGGING "</button></form></p>"
"<p><form action='co' method='get'><button>" D_CONFIGURE_OTHER "</button></form></p>"
"<p><form action='tp' method='get'><button>" D_CONFIGURE_TEMPLATE "</button></form></p>";
const char HTTP_BTN_RESET[] PROGMEM =
"<br/>"
"<form action='rt' method='get' onsubmit='return confirm(\"" D_CONFIRM_RESET_CONFIGURATION "\");'><button class='button bred'>" D_RESET_CONFIGURATION "</button></form>";
const char HTTP_BTN_MENU5[] PROGMEM =
"<p><form action='dl' method='get'><button>" D_BACKUP_CONFIGURATION "</button></form></p>"
"<p><form action='rs' method='get'><button>" D_RESTORE_CONFIGURATION "</button></form></p>";
const char HTTP_BTN_MAIN[] PROGMEM =
"<br/>"
"<form action='.' method='get'><button>" D_MAIN_MENU "</button></form>";
const char HTTP_FORM_LOGIN[] PROGMEM =
"<fieldset>"
"<form method='post' action='/'>"
@ -325,9 +294,7 @@ const char HTTP_FORM_LOGIN[] PROGMEM =
const char HTTP_FORM_TEMPLATE[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_TEMPLATE_PARAMETERS "&nbsp;</b></legend>"
"<form method='get' action='tp'>"
"<p><b>" D_TEMPLATE_NAME "</b><br/><input id='s1' name='s1' placeholder='" D_TEMPLATE_NAME "'></p>"
"<p></p><b>" D_BASE_TYPE "</b><br/><select id='g99' name='g99' onchange='st(this.value)'></select><br/>";
"<form method='get' action='tp'>";
const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM =
"<p></p>" // Keep close so do not use <br/>
"<fieldset><legend><b>&nbsp;" D_TEMPLATE_FLAGS "&nbsp;</b></legend><p>"
@ -403,7 +370,7 @@ const char HTTP_FORM_CMND[] PROGMEM =
"</form>";
const char HTTP_TABLE100[] PROGMEM =
"<table style='width:100%'>";
"<table style='width:100%%'>";
const char HTTP_COUNTER[] PROGMEM =
"<br/><div id='t' name='t' style='text-align:center;'></div>";
@ -418,6 +385,20 @@ const char HTTP_END[] PROGMEM =
const char HTTP_DEVICE_CONTROL[] PROGMEM = "<td style='width:%d%%'><button onclick='la(\"&o=%d\");'>%s%s</button></td>"; // ?o is related to WebGetArg("o", tmp, sizeof(tmp));
const char HTTP_DEVICE_STATE[] PROGMEM = "%s<td style='width:%d{c}%s;font-size:%dpx'>%s</div></td>"; // {c} = %'><div style='text-align:center;font-weight:
enum ButtonTitle {
BUTTON_RESTART, BUTTON_RESET_CONFIGURATION,
BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE,
BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE };
const char kButtonTitle[] PROGMEM =
D_RESTART "|" D_RESET_CONFIGURATION "|"
D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|"
D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION;
const char kButtonAction[] PROGMEM =
".|rt|"
".|cn|in|up|cs|"
"md|wi|lg|co|tp|dl|rs";
const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION;
enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM };
const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream";
@ -583,27 +564,6 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
return true;
}
String WSNetworkInfo(void)
{
String info = "";
if (Settings.flag3.gui_hostname_ip) {
uint8_t more_ips = 0;
info += F("<h3>"); info += my_hostname;
if (mdns_begun) { info += F(".local"); }
info += F(" (");
if (static_cast<uint32_t>(WiFi.localIP()) != 0) {
info += WiFi.localIP().toString();
more_ips++;
}
if (static_cast<uint32_t>(WiFi.softAPIP()) != 0) {
if (more_ips) { info += F(", "); }
info += WiFi.softAPIP().toString();
}
info += F(")</h3>");
}
return info;
}
void WSHeaderSend(void)
{
WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
@ -614,13 +574,21 @@ void WSHeaderSend(void)
#endif
}
/**********************************************************************************************
* HTTP Content Page handler
**********************************************************************************************/
void WSSend(int code, int ctype, const String& content)
{
char ct[25]; // strlen("application/octet-stream") +1 = Longest Content type string
WebServer->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content);
}
void WSContentSendLl(const String& content) // Low level sendContent for all core versions
/**********************************************************************************************
* HTTP Content Chunk handler
**********************************************************************************************/
void _WSContentSend(const String& content) // Low level sendContent for all core versions
{
size_t len = content.length();
@ -633,40 +601,21 @@ void WSContentSendLl(const String& content) // Low level sendContent for a
WebServer->sendContent(content);
#endif
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("WSContentSend"));
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WEB: Chunk size %d"), len);
#endif
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HTP: Chunk size %d"), len);
}
void WSContentFlush()
{
if (chunk_buffer.length() > 0) {
WSContentSendLl(chunk_buffer); // Flush chunk buffer
_WSContentSend(chunk_buffer); // Flush chunk buffer
chunk_buffer = "";
}
}
void WSContentSend(const String& content) // Content send string data
{
size_t len = content.length();
if (0 == len) { // No content
return;
}
else if (len < CHUNKED_BUFFER_SIZE) { // Append chunk buffer with small content
chunk_buffer += content;
len = chunk_buffer.length();
}
if (len >= CHUNKED_BUFFER_SIZE) { // Either content or chunk buffer is oversize
WSContentFlush(); // Send chunk buffer before possible content oversize
}
if (content.length() >= CHUNKED_BUFFER_SIZE) { // Content is oversize
WSContentSendLl(content); // Send content
}
}
void WSContentSend_P(PGM_P formatP, ...) // Content send snprintf_P char data
void WSContentSend_P(const char* formatP, ...) // Content send snprintf_P char data
{
// This uses char strings. Be aware of sending %% if % is needed
va_list arg;
@ -674,10 +623,26 @@ void WSContentSend_P(PGM_P formatP, ...) // Content send snprintf_P cha
int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg);
va_end(arg);
WSContentSend(mqtt_data);
if (0 == len) { // No content
return;
}
else if (len == sizeof(mqtt_data)) {
AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large"));
}
else if (len < CHUNKED_BUFFER_SIZE) { // Append chunk buffer with small content
chunk_buffer += mqtt_data;
len = chunk_buffer.length();
}
if (len >= CHUNKED_BUFFER_SIZE) { // Either content or chunk buffer is oversize
WSContentFlush(); // Send chunk buffer before possible content oversize
}
if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { // Content is oversize
_WSContentSend(mqtt_data); // Send content
}
}
void WSContentStart(const String& title, bool auth)
void WSContentStart_P(const char* title, bool auth)
{
if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) {
return WebServer->requestAuthentication();
@ -690,45 +655,83 @@ void WSContentStart(const String& title, bool auth)
WebServer->sendHeader(F("Accept-Ranges"),F("none"));
WebServer->sendHeader(F("Transfer-Encoding"),F("chunked"));
#endif
WSSend(200, CT_HTML, ""); // Signal start of chunked content
WSSend(200, CT_HTML, ""); // Signal start of chunked content
chunk_buffer = "";
WSContentSend_P(HTTP_HEAD, Settings.friendlyname[0], title.c_str());
char ctitle[strlen_P(title) +1];
strcpy_P(ctitle, title); // Get title from flash to RAM
WSContentSend_P(HTTP_HEAD, Settings.friendlyname[0], ctitle);
}
void WSContentStart(const String& title)
void WSContentStart_P(const char* title)
{
WSContentStart(title, true);
WSContentStart_P(title, true);
}
void WSContentSendStyle(const String& style)
void WSContentSendStyle_P(const char* style)
{
if (WifiIsInManagerMode()) {
if (WifiConfigCounter()) {
WSContentSend(FPSTR(HTTP_SCRIPT_COUNTER));
WSContentSend_P(HTTP_SCRIPT_COUNTER);
}
}
WSContentSend(FPSTR(HTTP_HEAD_STYLE1));
WSContentSend(FPSTR(HTTP_HEAD_STYLE2));
WSContentSend(style);
WSContentSend_P(HTTP_HEAD_STYLE3, ModuleName().c_str(), Settings.friendlyname[0], WSNetworkInfo().c_str());
WSContentSend_P(HTTP_HEAD_STYLE1);
WSContentSend_P(HTTP_HEAD_STYLE2);
WSContentSend_P(style);
WSContentSend_P(HTTP_HEAD_STYLE3, ModuleName().c_str(), Settings.friendlyname[0]);
if (Settings.flag3.gui_hostname_ip) {
bool lip = (static_cast<uint32_t>(WiFi.localIP()) != 0);
bool sip = (static_cast<uint32_t>(WiFi.softAPIP()) != 0);
WSContentSend_P(PSTR("<h4>%s%s (%s%s%s)</h4>"), // sonoff.local (192.168.2.12,192.168.4.1)
my_hostname,
(mdns_begun) ? ".local" : "",
(lip) ? WiFi.localIP().toString().c_str() : "",
(lip && sip) ? "," : "",
(sip) ? WiFi.softAPIP().toString().c_str() : "");
}
WSContentSend_P(PSTR("</div>"));
}
void WSContentSendStyle()
void WSContentSendStyle(void)
{
WSContentSendStyle(F(""));
WSContentSendStyle_P(PSTR(""));
}
void WSContentButton(uint8_t title_index)
{
char action[4];
char title[32];
if (title_index <= BUTTON_RESET_CONFIGURATION) {
char confirm[64];
WSContentSend_P(PSTR("<p><form action='%s' method='get' onsubmit='return confirm(\"%s\");'><button name='%s' class='button bred'>%s</button></form></p>"),
GetTextIndexed(action, sizeof(action), title_index, kButtonAction),
GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm),
(!title_index) ? "rst" : "non",
GetTextIndexed(title, sizeof(title), title_index, kButtonTitle));
} else {
WSContentSend_P(PSTR("<p><form action='%s' method='get'><button>%s</button></form></p>"),
GetTextIndexed(action, sizeof(action), title_index, kButtonAction),
GetTextIndexed(title, sizeof(title), title_index, kButtonTitle));
}
}
void WSContentSpaceButton(uint8_t title_index)
{
WSContentSend_P(PSTR("<div></div>")); // 5px padding
WSContentButton(title_index);
}
void WSContentEnd(void)
{
if (WifiIsInManagerMode()) {
if (WifiConfigCounter()) {
WSContentSend(FPSTR(HTTP_COUNTER));
WSContentSend_P(HTTP_COUNTER);
}
}
WSContentSend_P(HTTP_END, my_version);
WSContentFlush(); // Flush chunk buffer
WSContentSendLl(""); // Signal end of chunked content
WSContentFlush(); // Flush chunk buffer
_WSContentSend(""); // Signal end of chunked content
WebServer->client().stop();
}
@ -743,21 +746,21 @@ void WebRestart(uint8_t type)
bool reset_only = (HTTP_MANAGER_RESET_ONLY == webserver_state);
WSContentStart((type) ? FPSTR(S_SAVE_CONFIGURATION) : FPSTR(S_RESTART), !reset_only);
WSContentSend(FPSTR(HTTP_SCRIPT_RELOAD));
WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only);
WSContentSend_P(HTTP_SCRIPT_RELOAD);
WSContentSendStyle();
if (type) {
WSContentSend(F("<div style='text-align:center;'><b>" D_CONFIGURATION_SAVED "</b><br/>"));
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_CONFIGURATION_SAVED "</b><br/>"));
if (2 == type) {
WSContentSend(F("<br/>" D_TRYING_TO_CONNECT "<br/>"));
WSContentSend_P(PSTR("<br/>" D_TRYING_TO_CONNECT "<br/>"));
}
WSContentSend(F("</div>"));
WSContentSend_P(PSTR("</div>"));
}
WSContentSend(FPSTR(HTTP_MSG_RSTRT));
WSContentSend_P(HTTP_MSG_RSTRT);
if (HTTP_MANAGER == webserver_state || reset_only) {
webserver_state = HTTP_ADMIN;
} else {
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSpaceButton(BUTTON_MAIN);
}
WSContentEnd();
@ -769,15 +772,14 @@ void WebRestart(uint8_t type)
void HandleWifiLogin(void)
{
WSContentStart(FPSTR(D_CONFIGURE_WIFI), false); // false means show page no matter if the client has or has not credentials
WSContentStart_P(S_CONFIGURE_WIFI, false); // false means show page no matter if the client has or has not credentials
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_LOGIN));
WSContentSend_P(HTTP_FORM_LOGIN);
if (HTTP_MANAGER_RESET_ONLY == webserver_state) {
WSContentSend(F("<br/>"));
WSContentSend(FPSTR(HTTP_BTN_RSTRT));
WSContentSpaceButton(BUTTON_RESTART);
#ifndef FIRMWARE_MINIMAL
WSContentSend(FPSTR(HTTP_BTN_RESET));
WSContentSpaceButton(BUTTON_RESET_CONFIGURATION);
#endif // FIRMWARE_MINIMAL
}
@ -788,7 +790,7 @@ void HandleRoot(void)
{
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the page.
if (WebServer->hasArg("rstrt")) {
if (WebServer->hasArg("rst")) {
WebRestart(0);
return;
}
@ -798,7 +800,7 @@ void HandleRoot(void)
if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != webserver_state) {
HandleWifiLogin();
} else {
if (!(Settings.web_password[0] != 0) || ((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password ) || HTTP_MANAGER_RESET_ONLY == webserver_state)) {
if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == webserver_state)) {
HandleWifiConfiguration();
} else {
// wrong user and pass
@ -817,11 +819,11 @@ void HandleRoot(void)
char stemp[5];
WSContentStart(FPSTR(S_MAIN_MENU));
WSContentStart_P(S_MAIN_MENU);
WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh);
WSContentSendStyle();
WSContentSend(F("<div id='l1' name='l1'></div>"));
WSContentSend_P(PSTR("<div id='l1' name='l1'></div>"));
if (devices_present) {
if (light_type) {
if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) {
@ -829,8 +831,8 @@ void HandleRoot(void)
}
WSContentSend_P(HTTP_MSG_SLIDER2, Settings.light_dimmer);
}
WSContentSend(FPSTR(HTTP_TABLE100));
WSContentSend(F("<tr>"));
WSContentSend_P(HTTP_TABLE100);
WSContentSend_P(PSTR("<tr>"));
if (SONOFF_IFAN02 == my_module_type) {
WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, "");
for (uint8_t i = 0; i < MAX_FAN_SPEED; i++) {
@ -843,20 +845,20 @@ void HandleRoot(void)
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : "");
}
}
WSContentSend(F("</tr></table>"));
WSContentSend_P(PSTR("</tr></table>"));
}
if (SONOFF_BRIDGE == my_module_type) {
WSContentSend(FPSTR(HTTP_TABLE100));
WSContentSend(F("<tr>"));
WSContentSend_P(HTTP_TABLE100);
WSContentSend_P(PSTR("<tr>"));
uint8_t idx = 0;
for (uint8_t i = 0; i < 4; i++) {
if (idx > 0) { WSContentSend(F("</tr><tr>")); }
if (idx > 0) { WSContentSend_P(PSTR("</tr><tr>")); }
for (uint8_t j = 0; j < 4; j++) {
idx++;
WSContentSend_P(PSTR("<td style='width:25%%'><button onclick='la(\"&k=%d\");'>%d</button></td>"), idx, idx); // &k is related to WebGetArg("k", tmp, sizeof(tmp));
}
}
WSContentSend(F("</tr></table>"));
WSContentSend_P(PSTR("</tr></table>"));
}
#ifndef FIRMWARE_MINIMAL
@ -865,13 +867,15 @@ void HandleRoot(void)
#endif // Not FIRMWARE_MINIMAL
if (HTTP_ADMIN == webserver_state) {
#ifndef FIRMWARE_MINIMAL
WSContentSend(FPSTR(HTTP_BTN_CONF));
#ifdef FIRMWARE_MINIMAL
WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE);
#else
WSContentSend(F("<br/>"));
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentButton(BUTTON_INFORMATION);
WSContentButton(BUTTON_FIRMWARE_UPGRADE);
#endif // Not FIRMWARE_MINIMAL
WSContentSend(FPSTR(HTTP_BTN_MENU1));
WSContentSend(FPSTR(HTTP_BTN_RSTRT));
WSContentButton(BUTTON_CONSOLE);
WSContentButton(BUTTON_RESTART);
}
WSContentEnd();
}
@ -965,17 +969,24 @@ void HandleConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION);
WSContentStart(FPSTR(S_CONFIGURATION));
WSContentStart_P(S_CONFIGURATION);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_BTN_MENU_MODULE));
WSContentButton(BUTTON_MODULE);
WSContentButton(BUTTON_WIFI);
XdrvCall(FUNC_WEB_ADD_BUTTON);
XsnsCall(FUNC_WEB_ADD_BUTTON);
WSContentSend(FPSTR(HTTP_BTN_MENU4));
WSContentSend(FPSTR(HTTP_BTN_RESET));
WSContentSend(FPSTR(HTTP_BTN_MENU5));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentButton(BUTTON_LOGGING);
WSContentButton(BUTTON_OTHER);
WSContentButton(BUTTON_TEMPLATE);
WSContentSpaceButton(BUTTON_RESET_CONFIGURATION);
WSContentButton(BUTTON_BACKUP);
WSContentButton(BUTTON_RESTORE);
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
}
@ -1045,24 +1056,28 @@ void HandleTemplateConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE);
WSContentStart(FPSTR(S_CONFIGURE_TEMPLATE));
WSContentSend(FPSTR(HTTP_SCRIPT_MODULE_TEMPLATE));
WSContentSend(FPSTR(HTTP_SCRIPT_TEMPLATE));
WSContentStart_P(S_CONFIGURE_TEMPLATE);
WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE);
WSContentSend_P(HTTP_SCRIPT_TEMPLATE);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_TEMPLATE));
WSContentSend(F("<br/><table>"));
WSContentSend_P(HTTP_FORM_TEMPLATE);
WSContentSend_P(HTTP_TABLE100);
WSContentSend_P(PSTR("<tr><td><b>" D_TEMPLATE_NAME "</b></td><td style='width:200px'><input id='s1' name='s1' placeholder='" D_TEMPLATE_NAME "'></td></tr>"
"<tr><td><b>" D_BASE_TYPE "</b></td><td><select id='g99' name='g99' onchange='st(this.value)'></select></td></tr>"
"</table>"
"<hr/>"));
WSContentSend_P(HTTP_TABLE100);
for (uint8_t i = 0; i < 17; i++) {
if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11
WSContentSend_P(PSTR("<tr><td%s><b>" D_GPIO "%d</b></td><td>%s</td><td%s><select id='g%d' name='g%d'></select></td></tr>"),
(0==i)?" style='width:74px'":"", i, ((9==i)||(10==i))? "<font color='red'>ESP8285</font>" :"", (0==i)?" style='width:176px'":"", i, i);
bool esp8285 = ((9==i)||(10==i));
WSContentSend_P(PSTR("<tr><td><b>%s" D_GPIO "%d%s</b></td><td%s><select id='g%d' name='g%d'></select></td></tr>"),
(esp8285) ? "<font color='red'>" : "", i, (esp8285) ? "</font>" : "", (0==i) ? " style='width:200px'" : "", i, i);
}
}
WSContentSend(F("</table>"));
WSContentSend(FPSTR(HTTP_FORM_TEMPLATE_FLAG));
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(PSTR("</table>"));
WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG);
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -1149,15 +1164,15 @@ void HandleModuleConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE);
WSContentStart(FPSTR(S_CONFIGURE_MODULE));
WSContentSend(FPSTR(HTTP_SCRIPT_MODULE_TEMPLATE));
WSContentStart_P(S_CONFIGURE_MODULE);
WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE);
WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module);
for (uint8_t i = 0; i < sizeof(cmodule); i++) {
if (ValidGPIO(i, cmodule.io[i])) {
WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); // g0 - g16
}
}
WSContentSend(FPSTR(HTTP_SCRIPT_MODULE2));
WSContentSend_P(HTTP_SCRIPT_MODULE2);
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str());
for (uint8_t i = 0; i < sizeof(cmodule); i++) {
@ -1167,9 +1182,9 @@ void HandleModuleConfiguration(void)
(WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? "<font color='red'>ESP8285</font>" :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i, i);
}
}
WSContentSend(F("</table>"));
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(PSTR("</table>"));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -1226,8 +1241,8 @@ void HandleWifiConfiguration(void)
return;
}
WSContentStart(FPSTR(S_CONFIGURE_WIFI), !WifiIsInManagerMode());
WSContentSend(FPSTR(HTTP_SCRIPT_WIFI));
WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode());
WSContentSend_P(HTTP_SCRIPT_WIFI);
WSContentSendStyle();
if (HTTP_MANAGER_RESET_ONLY != webserver_state) {
@ -1240,8 +1255,8 @@ void HandleWifiConfiguration(void)
if (0 == n) {
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND);
WSContentSend(FPSTR(S_NO_NETWORKS_FOUND));
WSContentSend(F(". " D_REFRESH_TO_SCAN_AGAIN "."));
WSContentSend_P(S_NO_NETWORKS_FOUND);
WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN "."));
} else {
//sort networks
int indices[n];
@ -1280,40 +1295,37 @@ void HandleWifiConfiguration(void)
int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i]));
if (minimum_signal_quality == -1 || minimum_signal_quality < quality) {
String item = F("<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;({w})&nbsp<span class='q'>{i} {r}%</span></div>");
String rssiQ;
rssiQ += quality;
item.replace(F("{v}"), htmlEscape(WiFi.SSID(indices[i])));
item.replace(F("{w}"), String(WiFi.channel(indices[i])));
item.replace(F("{r}"), rssiQ);
uint8_t auth = WiFi.encryptionType(indices[i]);
item.replace(F("{i}"), (ENC_TYPE_WEP == auth) ? F(D_WEP) : (ENC_TYPE_TKIP == auth) ? F(D_WPA_PSK) : (ENC_TYPE_CCMP == auth) ? F(D_WPA2_PSK) : (ENC_TYPE_AUTO == auth) ? F(D_AUTO) : F(""));
WSContentSend(item);
WSContentSend_P(PSTR("<div><a href='#p' onclick='c(this)'>%s</a>&nbsp;(%d)&nbsp<span class='q'>%s %d%%</span></div>"),
htmlEscape(WiFi.SSID(indices[i])).c_str(),
WiFi.channel(indices[i]),
(ENC_TYPE_WEP == auth) ? D_WEP : (ENC_TYPE_TKIP == auth) ? D_WPA_PSK : (ENC_TYPE_CCMP == auth) ? D_WPA2_PSK : (ENC_TYPE_AUTO == auth) ? D_AUTO : "",
quality
);
delay(0);
} else {
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SKIPPING_LOW_QUALITY));
}
}
WSContentSend(F("<br/>"));
WSContentSend_P(PSTR("<br/>"));
}
} else {
WSContentSend(F("<div><a href='/wi?scan='>" D_SCAN_FOR_WIFI_NETWORKS "</a></div><br/>"));
WSContentSend_P(PSTR("<div><a href='/wi?scan='>" D_SCAN_FOR_WIFI_NETWORKS "</a></div><br/>"));
}
// As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception
WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname);
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend_P(HTTP_FORM_END);
}
if (WifiIsInManagerMode()) {
WSContentSend(F("<br/>"));
WSContentSend(FPSTR(HTTP_BTN_RSTRT));
WSContentSpaceButton(BUTTON_RESTART);
#ifndef FIRMWARE_MINIMAL
WSContentSend(FPSTR(HTTP_BTN_RESET));
WSContentSpaceButton(BUTTON_RESET_CONFIGURATION);
#endif // FIRMWARE_MINIMAL
} else {
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSpaceButton(BUTTON_CONFIGURATION);
}
WSContentEnd();
}
@ -1352,9 +1364,9 @@ void HandleLoggingConfiguration(void)
return;
}
WSContentStart(FPSTR(S_CONFIGURE_LOGGING));
WSContentStart_P(S_CONFIGURE_LOGGING);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_LOG1));
WSContentSend_P(HTTP_FORM_LOG1);
char stemp1[32];
char stemp2[32];
uint8_t dlevel[3] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE };
@ -1369,11 +1381,11 @@ void HandleLoggingConfiguration(void)
(i == llevel) ? " selected" : "", i, i,
GetTextIndexed(stemp1, sizeof(stemp1), i, kLoggingLevels));
}
WSContentSend(F("</select></p>"));
WSContentSend_P(PSTR("</select></p>"));
}
WSContentSend_P(HTTP_FORM_LOG2, Settings.syslog_host, Settings.syslog_port, Settings.tele_period);
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -1416,7 +1428,7 @@ void HandleOtherConfiguration(void)
return;
}
WSContentStart(FPSTR(S_CONFIGURE_OTHER));
WSContentStart_P(S_CONFIGURE_OTHER);
WSContentSendStyle();
TemplateJson();
@ -1437,7 +1449,7 @@ void HandleOtherConfiguration(void)
}
#ifdef USE_EMULATION
WSContentSend(F("<p></p><fieldset><legend><b>&nbsp;" D_EMULATION "&nbsp;</b></legend><p>")); // Keep close to Friendlynames so do not use <br/>
WSContentSend_P(PSTR("<p></p><fieldset><legend><b>&nbsp;" D_EMULATION "&nbsp;</b></legend><p>")); // Keep close to Friendlynames so do not use <br/>
for (uint8_t i = 0; i < EMUL_MAX; i++) {
WSContentSend_P(PSTR("<input id='r%d' name='b2' type='radio' value='%d'%s><b>%s</b> %s<br/>"), // Different id only used for labels
i, i,
@ -1445,11 +1457,11 @@ void HandleOtherConfiguration(void)
GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions),
(i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE);
}
WSContentSend(F("</p></fieldset>"));
WSContentSend_P(PSTR("</p></fieldset>"));
#endif // USE_EMULATION
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -1541,11 +1553,11 @@ void HandleResetConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION);
WSContentStart(FPSTR(S_RESET_CONFIGURATION), !WifiIsInManagerMode());
WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode());
WSContentSendStyle();
WSContentSend(F("<div style='text-align:center;'>" D_CONFIGURATION_RESET "</div>"));
WSContentSend(FPSTR(HTTP_MSG_RSTRT));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSend_P(PSTR("<div style='text-align:center;'>" D_CONFIGURATION_RESET "</div>"));
WSContentSend_P(HTTP_MSG_RSTRT);
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
char command[CMDSZ];
@ -1559,11 +1571,11 @@ void HandleRestoreConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION);
WSContentStart(FPSTR(S_RESTORE_CONFIGURATION));
WSContentStart_P(S_RESTORE_CONFIGURATION);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_RST));
WSContentSend_P(HTTP_FORM_RST);
WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE);
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
upload_error = 0;
@ -1582,11 +1594,11 @@ void HandleInformation(void)
int freeMem = ESP.getFreeHeap();
WSContentStart(FPSTR(S_INFORMATION));
WSContentStart_P(S_INFORMATION);
// Save 1k of code space replacing table html with javascript replace codes
// }1 = </td></tr><tr><th>
// }2 = </th><td>
WSContentSend(FPSTR(HTTP_SCRIPT_INFO_BEGIN));
WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN);
WSContentSend_P(PSTR("<table style='width:100%%'><tr><th>"));
WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image);
WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str());
@ -1659,13 +1671,13 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024);
WSContentSend_P(PSTR("</td></tr></table>"));
WSContentSend(FPSTR(HTTP_SCRIPT_INFO_END));
WSContentSend_P(HTTP_SCRIPT_INFO_END);
WSContentSendStyle();
// WSContentSend(F("<fieldset><legend><b>&nbsp;Information&nbsp;</b></legend>"));
// WSContentSend_P(PSTR("<fieldset><legend><b>&nbsp;Information&nbsp;</b></legend>"));
WSContentSend_P(PSTR("<style>td{padding:0px 5px;}</style>"
"<div id='i' name='i'></div>"));
// WSContentSend(F("</fieldset>"));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
// WSContentSend_P(PSTR("</fieldset>"));
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
}
#endif // Not FIRMWARE_MINIMAL
@ -1678,11 +1690,11 @@ void HandleUpgradeFirmware(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE);
WSContentStart(FPSTR(S_FIRMWARE_UPGRADE));
WSContentStart_P(S_FIRMWARE_UPGRADE);
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_UPG, Settings.ota_url);
WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE);
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
upload_error = 0;
@ -1705,12 +1717,12 @@ void HandleUpgradeFirmwareStart(void)
ExecuteWebCommand(command, SRC_WEBGUI);
}
WSContentStart(FPSTR(S_INFORMATION));
WSContentSend(FPSTR(HTTP_SCRIPT_RELOAD_OTA));
WSContentStart_P(S_INFORMATION);
WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA);
WSContentSendStyle();
WSContentSend(F("<div style='text-align:center;'><b>" D_UPGRADE_STARTED " ...</b></div>"));
WSContentSend(FPSTR(HTTP_MSG_RSTRT));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_UPGRADE_STARTED " ...</b></div>"));
WSContentSend_P(HTTP_MSG_RSTRT);
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1"));
@ -1729,14 +1741,14 @@ void HandleUploadDone(void)
restart_flag = 0;
MqttRetryCounter(0);
WSContentStart(FPSTR(S_INFORMATION));
WSContentStart_P(S_INFORMATION);
if (!upload_error) {
WSContentSend(FPSTR(HTTP_SCRIPT_RELOAD_OTA)); // Refesh main web ui after OTA upgrade
WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); // Refesh main web ui after OTA upgrade
}
WSContentSendStyle();
WSContentSend(F("<div style='text-align:center;'><b>" D_UPLOAD " <font color='"));
WSContentSend_P(PSTR("<div style='text-align:center;'><b>" D_UPLOAD " <font color='"));
if (upload_error) {
WSContentSend(F("red'>" D_FAILED "</font></b><br/><br/>"));
WSContentSend_P(PSTR("red'>" D_FAILED "</font></b><br/><br/>"));
#ifdef USE_RF_FLASH
if (upload_error < 14) {
#else
@ -1746,18 +1758,18 @@ void HandleUploadDone(void)
} else {
snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), upload_error);
}
WSContentSend(error);
WSContentSend_P(error);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_UPLOAD ": %s"), error);
stop_flash_rotate = Settings.flag.stop_flash_rotate;
} else {
WSContentSend(F("green'>" D_SUCCESSFUL "</font></b><br/>"));
WSContentSend(FPSTR(HTTP_MSG_RSTRT));
WSContentSend_P(PSTR("green'>" D_SUCCESSFUL "</font></b><br/>"));
WSContentSend_P(HTTP_MSG_RSTRT);
ShowWebSource(SRC_WEBGUI);
restart_flag = 2; // Always restart to re-enable disabled features during update
}
SettingsBufferFree();
WSContentSend(F("</div><br/>"));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSend_P(PSTR("</div><br/>"));
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
}
@ -2032,11 +2044,11 @@ void HandleConsole(void)
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE);
WSContentStart(FPSTR(S_CONSOLE));
WSContentStart_P(S_CONSOLE);
WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_CMND));
WSContentSend(FPSTR(HTTP_BTN_MAIN));
WSContentSend_P(HTTP_FORM_CMND);
WSContentSpaceButton(BUTTON_MAIN);
WSContentEnd();
}

View File

@ -18,41 +18,6 @@
*/
#define XDRV_02 2
/*********************************************************************************************\
* Select ONE of possible MQTT library types below
\*********************************************************************************************/
// Default MQTT driver for both non-TLS and TLS connections. Blocks network if MQTT server is unavailable.
//#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library
// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support
//#define MQTT_LIBRARY_TYPE MQTT_TASMOTAMQTT // Use TasmotaMqtt library (+4k4 (core 2.3.0), +14k4 (core 2.4.2 lwip2) code, +4k mem) - non-TLS only
// Alternative MQTT driver does not block network when MQTT server is unavailable. TLS should work but needs to be tested.
//#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Use arduino-mqtt (lwmqtt) library (+3k3 code, +2k mem)
#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete as of v6.2.1.11
#undef MQTT_LIBRARY_TYPE
#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT
#endif
/*
#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
#undef MQTT_LIBRARY_TYPE
#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Obsolete in near future
#endif
*/
#ifdef USE_MQTT_TLS
#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
#undef MQTT_LIBRARY_TYPE
#endif
#ifndef MQTT_LIBRARY_TYPE
#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library as it only supports TLS
#endif
#endif // USE_MQTT_TLS
/*********************************************************************************************/
#ifdef USE_MQTT_TLS
@ -89,8 +54,6 @@ bool mqtt_allowed = false; // MQTT enabled and parameters valid
* void MqttLoop()
\*********************************************************************************************/
#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) /***********************************************/
#include <PubSubClient.h>
// Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength)
@ -134,96 +97,6 @@ void MqttLoop(void)
MqttClient.loop();
}
#elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) /**********************************************/
#include <TasmotaMqtt.h>
TasmotaMqtt MqttClient;
bool MqttIsConnected(void)
{
return MqttClient.Connected();
}
void MqttDisconnect(void)
{
MqttClient.Disconnect();
}
void MqttDisconnectedCb(void)
{
MqttDisconnected(MqttClient.State()); // status codes are documented in file mqtt.h as tConnState
}
void MqttSubscribeLib(const char *topic)
{
MqttClient.Subscribe(topic, 0);
}
void MqttUnsubscribeLib(const char *topic)
{
MqttClient.Unsubscribe(topic, 0);
}
bool MqttPublishLib(const char* topic, bool retained)
{
return MqttClient.Publish(topic, mqtt_data, strlen(mqtt_data), 0, retained);
}
void MqttLoop(void)
{
}
#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) /**********************************************/
#include <MQTTClient.h>
MQTTClient MqttClient(MQTT_MAX_PACKET_SIZE);
bool MqttIsConnected(void)
{
return MqttClient.connected();
}
void MqttDisconnect(void)
{
MqttClient.disconnect();
}
/*
void MqttMyDataCb(MQTTClient* client, char* topic, char* data, int data_len)
//void MqttMyDataCb(MQTTClient *client, char topic[], char data[], int data_len)
{
// MqttDataHandler((char*)topic, (uint8_t*)data, data_len);
}
*/
void MqttMyDataCb(String &topic, String &data)
{
MqttDataHandler((char*)topic.c_str(), (uint8_t*)data.c_str(), data.length());
}
void MqttSubscribeLib(const char *topic)
{
MqttClient.subscribe(topic, 0);
}
void MqttUnsubscribeLib(const char *topic)
{
MqttClient.unsubscribe(topic, 0);
}
bool MqttPublishLib(const char* topic, bool retained)
{
return MqttClient.publish(topic, mqtt_data, strlen(mqtt_data), retained, 0);
}
void MqttLoop(void)
{
MqttClient.loop();
// delay(10);
}
#endif // MQTT_LIBRARY_TYPE
/*********************************************************************************************/
#ifdef USE_DISCOVERY
@ -254,11 +127,6 @@ void MqttDiscoverServer(void)
#endif // MQTT_HOST_DISCOVERY
#endif // USE_DISCOVERY
int MqttLibraryType(void)
{
return (int)MQTT_LIBRARY_TYPE;
}
void MqttRetryCounter(uint8_t value)
{
mqtt_retry_counter = value;
@ -281,7 +149,9 @@ void MqttPublishDirect(const char* topic, bool retained)
char sretained[CMDSZ];
char slog_type[10];
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("MqttPublishDirect"));
#endif
sretained[0] = '\0';
snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT));
@ -584,25 +454,9 @@ void MqttReconnect(void)
if (!MqttCheckTls()) return;
#endif // USE_MQTT_TLS
#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
MqttClient.InitConnection(Settings.mqtt_host, Settings.mqtt_port);
MqttClient.InitClient(mqtt_client, mqtt_user, mqtt_pwd, MQTT_KEEPALIVE);
MqttClient.InitLWT(stopic, mqtt_data, 1, true);
MqttClient.OnConnected(MqttConnected);
MqttClient.OnDisconnected(MqttDisconnectedCb);
MqttClient.OnData(MqttDataHandler);
#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT)
MqttClient.begin(Settings.mqtt_host, Settings.mqtt_port, EspClient);
MqttClient.setWill(stopic, mqtt_data, true, 1);
MqttClient.setOptions(MQTT_KEEPALIVE, true, MQTT_TIMEOUT);
// MqttClient.onMessageAdvanced(MqttMyDataCb);
MqttClient.onMessage(MqttMyDataCb);
#endif
mqtt_initial_connection_state = 1;
}
#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT)
MqttClient.setCallback(MqttDataHandler);
MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);
if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data)) {
@ -610,15 +464,6 @@ void MqttReconnect(void)
} else {
MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state
}
#elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT)
MqttClient.Connect();
#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT)
if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd)) {
MqttConnected();
} else {
MqttDisconnected(MqttClient.lastError()); // status codes are documented here https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11
}
#endif // MQTT_LIBRARY_TYPE
}
void MqttCheck(void)
@ -917,7 +762,7 @@ void HandleMqttConfiguration(void)
char str[sizeof(Settings.mqtt_client)];
WSContentStart(FPSTR(S_CONFIGURE_MQTT));
WSContentStart_P(S_CONFIGURE_MQTT);
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_MQTT1,
Settings.mqtt_host,
@ -930,8 +775,8 @@ void HandleMqttConfiguration(void)
Settings.mqtt_topic,
MQTT_FULLTOPIC, MQTT_FULLTOPIC,
Settings.mqtt_fulltopic);
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -980,7 +825,7 @@ bool Xdrv02(uint8_t function)
switch (function) {
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_BUTTON:
WSContentSend(FPSTR(HTTP_BTN_MENU_MQTT));
WSContentSend_P(HTTP_BTN_MENU_MQTT);
break;
case FUNC_WEB_ADD_HANDLER:
WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration);

View File

@ -114,7 +114,7 @@ void IrReceiveCheck(void)
if (irrecv->decode(&results)) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value %08X, Decode %d"),
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%08X, Decode %d"),
irsend_active, results.rawlen, results.overflow, results.bits, results.value, results.decode_type);
unsigned long now = millis();
@ -129,7 +129,7 @@ void IrReceiveCheck(void)
if (Settings.flag.ir_receive_decimal) {
snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)results.value);
} else {
snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)results.value);
snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)results.value);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"),
GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, stemp);

View File

@ -434,7 +434,6 @@ const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM =
const char HTTP_FORM_DOMOTICZ[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_DOMOTICZ_PARAMETERS "&nbsp;</b></legend>"
"<form method='post' action='" WEB_HANDLE_DOMOTICZ "'>"
"<br/>"
"<table>";
const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM =
"<tr><td style='width:260px'><b>" D_DOMOTICZ_IDX " %d</b></td><td style='width:70px'><input id='r%d' name='r%d' placeholder='0' value='%d'></td></tr>"
@ -460,9 +459,9 @@ void HandleDomoticzConfiguration(void)
char stemp[32];
WSContentStart(FPSTR(S_CONFIGURE_DOMOTICZ));
WSContentStart_P(S_CONFIGURE_DOMOTICZ);
WSContentSendStyle();
WSContentSend(FPSTR(HTTP_FORM_DOMOTICZ));
WSContentSend_P(HTTP_FORM_DOMOTICZ);
for (int i = 0; i < MAX_DOMOTICZ_IDX; i++) {
if (i < devices_present) {
WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY,
@ -480,9 +479,9 @@ void HandleDomoticzConfiguration(void)
i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, i, Settings.domoticz_sensor_idx[i]);
}
WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer);
WSContentSend(F("</table>"));
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(PSTR("</table>"));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -533,7 +532,7 @@ bool Xdrv07(uint8_t function)
switch (function) {
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_BUTTON:
WSContentSend(FPSTR(HTTP_BTN_MENU_DOMOTICZ));
WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ);
break;
case FUNC_WEB_ADD_HANDLER:
WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration);

View File

@ -150,7 +150,7 @@ bool SerialBridgeCommand(void)
else if (CMND_SBAUDRATE == command_code) {
char *p;
int baud = strtol(XdrvMailbox.data, &p, 10);
if (baud > 0) {
if (baud >= 1200) {
baud /= 1200; // Make it a valid baudrate
Settings.sbaudrate = (1 == XdrvMailbox.payload) ? SOFT_BAUDRATE / 1200 : baud;
SerialBridgeSerial->begin(Settings.sbaudrate * 1200); // Reinitialize serial port with new baud rate

View File

@ -538,7 +538,7 @@ const char HTTP_TIMER_SCRIPT2[] PROGMEM =
"if(m==0){" // Time is set
"so(0);" // Hide offset span and allow Hour 00..23
"q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" // Set hours
"q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes
"q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes
"}"
"if((m==1)||(m==2)){" // Sunrise or sunset is set
"so(1);" // Show offset span and allow Hour 00..11
@ -546,7 +546,7 @@ const char HTTP_TIMER_SCRIPT2[] PROGMEM =
"if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" // Negative offset
"else{qs('#dr').selectedIndex=0;}"
"if(q<10){q='0'+q;}qs('#ho').value=q;" // Set offset hours
"q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set offset minutes
"q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set offset minutes
"}"
"}"
"function so(b){" // Hide or show offset items
@ -648,7 +648,7 @@ const char HTTP_TIMER_SCRIPT6[] PROGMEM =
"}"
"window.onload=it;";
const char HTTP_TIMER_STYLE[] PROGMEM =
".tl{float:left;border-radius:0;border:1px solid #f2f2f2;padding:1px;width:6.25%;}"; // Border color needs to be the same as Fieldset background color from HTTP_HEAD_STYLE1 (transparent won't work)
".tl{float:left;border-radius:0;border:1px solid #f2f2f2;padding:1px;width:6.25%%;}"; // Border color needs to be the same as Fieldset background color from HTTP_HEAD_STYLE1 (transparent won't work)
const char HTTP_FORM_TIMER1[] PROGMEM =
"<fieldset style='min-width:470px;text-align:center;'>"
"<legend style='text-align:left;'><b>&nbsp;" D_TIMER_PARAMETERS "&nbsp;</b></legend>"
@ -698,29 +698,29 @@ void HandleTimerConfiguration(void)
return;
}
WSContentStart(FPSTR(S_CONFIGURE_TIMER));
WSContentSend(FPSTR(HTTP_TIMER_SCRIPT1));
WSContentStart_P(S_CONFIGURE_TIMER);
WSContentSend_P(HTTP_TIMER_SCRIPT1);
#ifdef USE_SUNRISE
WSContentSend(FPSTR(HTTP_TIMER_SCRIPT2));
WSContentSend_P(HTTP_TIMER_SCRIPT2);
#endif // USE_SUNRISE
WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present);
WSContentSend_P(HTTP_TIMER_SCRIPT4, devices_present);
WSContentSend_P(HTTP_TIMER_SCRIPT5, devices_present);
WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present);
WSContentSendStyle(FPSTR(HTTP_TIMER_STYLE));
WSContentSendStyle_P(HTTP_TIMER_STYLE);
WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : "");
for (uint8_t i = 0; i < MAX_TIMERS; i++) {
WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data);
}
WSContentSend(FPSTR(HTTP_FORM_TIMER2));
WSContentSend_P(HTTP_FORM_TIMER2);
#ifdef USE_SUNRISE
WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str());
#else
WSContentSend(FPSTR(HTTP_FORM_TIMER3));
WSContentSend_P(HTTP_FORM_TIMER3);
#endif // USE_SUNRISE
WSContentSend(FPSTR(HTTP_FORM_TIMER4));
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(HTTP_FORM_TIMER4);
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -764,9 +764,9 @@ bool Xdrv09(uint8_t function)
#ifdef USE_TIMERS_WEB
case FUNC_WEB_ADD_BUTTON:
#ifdef USE_RULES
WSContentSend(FPSTR(HTTP_BTN_MENU_TIMER));
WSContentSend_P(HTTP_BTN_MENU_TIMER);
#else
if (devices_present) { WSContentSend(FPSTR(HTTP_BTN_MENU_TIMER)); }
if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); }
#endif // USE_RULES
break;
case FUNC_WEB_ADD_HANDLER:

View File

@ -387,7 +387,9 @@ bool RulesProcessEvent(char *json_event)
{
bool serviced = false;
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("RulesProcessEvent"));
#endif
String event_saved = json_event;
event_saved.toUpperCase();

View File

@ -762,7 +762,7 @@ const char HTTP_FORM_KNX2[] PROGMEM =
"<fieldset><center>"
"<b>" D_KNX_GROUP_ADDRESS_TO_WRITE "</b><hr>"
"<select name='GAop' style='width:25%;'>";
"<select name='GAop' style='width:25%%;'>";
const char HTTP_FORM_KNX_OPT[] PROGMEM =
"<option value='%d'>%s</option>";
@ -786,7 +786,7 @@ const char HTTP_FORM_KNX3[] PROGMEM =
"<b>" D_KNX_GROUP_ADDRESS_TO_READ "</b><hr>";
const char HTTP_FORM_KNX4[] PROGMEM =
"-> <select name='CBop' style='width:25%;'>";
"-> <select name='CBop' style='width:25%%;'>";
const char HTTP_FORM_KNX_ADD_TABLE_ROW2[] PROGMEM =
"<tr><td><b>%d / %d / %d -> %s</b></td>"
@ -859,33 +859,33 @@ void HandleKNXConfiguration(void)
}
WSContentStart(FPSTR(S_CONFIGURE_KNX));
WSContentSend(
F("function GAwarning()"
"{"
"var GA_FNUM = eb('GA_FNUM');"
"var GA_AREA = eb('GA_AREA');"
"var GA_FDEF = eb('GA_FDEF');"
"if ( GA_FNUM != null && GA_FNUM.value == '0' && GA_AREA.value == '0' && GA_FDEF.value == '0' ) {"
"alert('" D_KNX_WARNING "');"
WSContentStart_P(S_CONFIGURE_KNX);
WSContentSend_P(
PSTR("function GAwarning()"
"{"
"var GA_FNUM=eb('GA_FNUM');"
"var GA_AREA=eb('GA_AREA');"
"var GA_FDEF=eb('GA_FDEF');"
"if(GA_FNUM!=null&&GA_FNUM.value=='0'&&GA_AREA.value=='0'&&GA_FDEF.value=='0'){"
"alert('" D_KNX_WARNING "');"
"}"
"}"
"}"
"function CBwarning()"
"{"
"var CB_FNUM = eb('CB_FNUM');"
"var CB_AREA = eb('CB_AREA');"
"var CB_FDEF = eb('CB_FDEF');"
"if ( CB_FNUM != null && CB_FNUM.value == '0' && CB_AREA.value == '0' && CB_FDEF.value == '0' ) {"
"alert('" D_KNX_WARNING "');"
"}"
"}"));
"function CBwarning()"
"{"
"var CB_FNUM=eb('CB_FNUM');"
"var CB_AREA=eb('CB_AREA');"
"var CB_FDEF=eb('CB_FDEF');"
"if(CB_FNUM!=null&&CB_FNUM.value=='0'&&CB_AREA.value=='0'&&CB_FDEF.value=='0'){"
"alert('" D_KNX_WARNING "');"
"}"
"}"));
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_KNX, KNX_physs_addr.pa.area, KNX_physs_addr.pa.line, KNX_physs_addr.pa.member);
if ( Settings.flag.knx_enabled ) { WSContentSend(F(" checked")); }
WSContentSend(FPSTR(HTTP_FORM_KNX1));
if ( Settings.flag.knx_enable_enhancement ) { WSContentSend(F(" checked")); }
if ( Settings.flag.knx_enabled ) { WSContentSend_P(PSTR(" checked")); }
WSContentSend_P(HTTP_FORM_KNX1);
if ( Settings.flag.knx_enable_enhancement ) { WSContentSend_P(PSTR(" checked")); }
WSContentSend(FPSTR(HTTP_FORM_KNX2));
WSContentSend_P(HTTP_FORM_KNX2);
for (uint8_t i = 0; i < KNX_MAX_device_param ; i++)
{
if ( device_param[i].show )
@ -893,7 +893,7 @@ void HandleKNXConfiguration(void)
WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_ga[i]);
}
}
WSContentSend(F("</select> -> "));
WSContentSend_P(PSTR("</select> -> "));
WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_FNUM", "GA_AREA", "GA_AREA", "GA_FDEF", "GA_FDEF");
WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1);
for (uint8_t i = 0; i < Settings.knx_GA_registered ; ++i)
@ -905,9 +905,9 @@ void HandleKNXConfiguration(void)
}
}
WSContentSend(FPSTR(HTTP_FORM_KNX3));
WSContentSend_P(HTTP_FORM_KNX3);
WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_FNUM", "CB_AREA", "CB_AREA", "CB_FDEF", "CB_FDEF");
WSContentSend(FPSTR(HTTP_FORM_KNX4));
WSContentSend_P(HTTP_FORM_KNX4);
uint8_t j;
for (uint8_t i = 0; i < KNX_MAX_device_param ; i++)
@ -920,7 +920,7 @@ void HandleKNXConfiguration(void)
WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]);
}
}
WSContentSend(F("</select> "));
WSContentSend_P(PSTR("</select> "));
WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2);
for (uint8_t i = 0; i < Settings.knx_CB_registered ; ++i)
@ -931,9 +931,9 @@ void HandleKNXConfiguration(void)
WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1);
}
}
WSContentSend(F("</table></center></fieldset>"));
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(PSTR("</table></center></fieldset>"));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -1229,7 +1229,7 @@ bool Xdrv11(uint8_t function)
#ifdef USE_WEBSERVER
#ifdef USE_KNX_WEB_MENU
case FUNC_WEB_ADD_BUTTON:
WSContentSend(FPSTR(HTTP_BTN_MENU_KNX));
WSContentSend_P(HTTP_BTN_MENU_KNX);
break;
case FUNC_WEB_ADD_HANDLER:
WebServer->on("/kn", HandleKNXConfiguration);

View File

@ -166,11 +166,12 @@ static void Shorten(char** s, char *prefix)
}
}
int try_snprintf_P(char *s, size_t n, const char *format, ... )
void try_snprintf_P(char *s, int n, const char *format, ... )
{
va_list args;
va_start(args, format);
int len = vsnprintf_P(NULL, 0, format, args);
char dummy[2];
int len = vsnprintf_P(dummy, 1, format, args);
if (len >= n) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR("ERROR: MQTT discovery failed due to too long topic or friendly name. "
"Please shorten topic and friendly name. Failed to format(%u/%u):"), len, n);
@ -232,6 +233,7 @@ void HAssAnnounceRelayLight(void)
Shorten(&command_topic, prefix);
Shorten(&state_topic, prefix);
Shorten(&availability_topic, prefix);
try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_RELAY,
name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data,
@ -542,11 +544,11 @@ void HAssPublishStatus(void)
PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\","
"\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
"\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\","
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\","
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,"
"\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\","
"\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"),
my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(),
GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(),
GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(),
Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(),
WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg);
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE));

View File

@ -695,7 +695,9 @@ void DisplayJsonValue(const char* topic, const char* device, const char* mkey, c
char source[Settings.display_cols[0] - Settings.display_cols[1]];
char svalue[Settings.display_cols[1] +1];
#ifdef USE_DEBUG_DRIVER
ShowFreeMem(PSTR("DisplayJsonValue"));
#endif
memset(spaces, 0x20, sizeof(spaces));
spaces[sizeof(spaces) -1] = '\0';

View File

@ -49,7 +49,7 @@ void RfReceiveCheck(void)
int protocol = mySwitch.getReceivedProtocol();
int delay = mySwitch.getReceivedDelay();
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data %lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay);
uint32_t now = millis();
if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) {
@ -59,7 +59,7 @@ void RfReceiveCheck(void)
if (Settings.flag.rf_receive_decimal) { // SetOption28 (0 = hexadecimal, 1 = decimal)
snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data);
} else {
snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)data);
snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"),
stemp, bits, protocol, delay);

View File

@ -192,20 +192,6 @@ bool (* const xdrv_func_ptr[])(uint8_t) = { // Driver Function Pointers
const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); // Number of drivers found
bool XdrvCommand(bool grpflg, char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, uint16_t payload16)
{
// XdrvMailbox.valid = 1;
XdrvMailbox.index = index;
XdrvMailbox.data_len = data_len;
XdrvMailbox.payload16 = payload16;
XdrvMailbox.payload = payload;
XdrvMailbox.grpflg = grpflg;
XdrvMailbox.topic = type;
XdrvMailbox.data = dataBuf;
return XdrvCall(FUNC_COMMAND);
}
bool XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf)
{
XdrvMailbox.index = stopicBuf;
@ -221,6 +207,7 @@ bool XdrvRulesProcess(void)
return XdrvCall(FUNC_RULES_PROCESS);
}
#ifdef USE_DEBUG_DRIVER
void ShowFreeMem(const char *where)
{
char stemp[20];
@ -228,6 +215,7 @@ void ShowFreeMem(const char *where)
XdrvMailbox.data = stemp;
XdrvCall(FUNC_FREE_MEM);
}
#endif
/*********************************************************************************************\
* Function call to all xdrv
@ -242,6 +230,7 @@ bool XdrvCall(uint8_t Function)
result = xdrv_func_ptr[x](Function);
if (result && ((FUNC_COMMAND == Function) ||
(FUNC_COMMAND_DRIVER == Function) ||
(FUNC_MQTT_DATA == Function) ||
(FUNC_RULES_PROCESS == Function) ||
(FUNC_BUTTON_PRESSED == Function) ||

View File

@ -266,7 +266,7 @@ bool Xsns13(uint8_t function)
if (i2c_flg) {
switch (function) {
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if ((XSNS_13 == XdrvMailbox.index) && (ina219_type)) {
result = Ina219CommandSensor();
}

View File

@ -373,7 +373,7 @@ bool Xsns15(uint8_t function)
case FUNC_EVERY_SECOND:
MhzEverySecond();
break;
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_15 == XdrvMailbox.index) {
result = MhzCommandSensor();
}

View File

@ -28,6 +28,8 @@
* - https://www.dfrobot.com/wiki/index.php/Weather-proof_Ultrasonic_Sensor_SKU_:_SEN0207
\*********************************************************************************************/
#define XSNS_22 22
uint8_t sr04_echo_pin = 0;
uint8_t sr04_trig_pin = 0;
real64_t distance;
@ -73,8 +75,6 @@ void Sr04Show(bool json)
* Interface
\*********************************************************************************************/
#define XSNS_22
bool Xsns22(uint8_t function)
{
bool result = false;

View File

@ -2046,7 +2046,7 @@ bool Xsns27(uint8_t function)
case FUNC_EVERY_50_MSECOND:
APDS9960_loop();
break;
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_27 == XdrvMailbox.index) {
result = APDS9960CommandSensor();
}

View File

@ -814,7 +814,7 @@ bool Xsns29(uint8_t function)
case FUNC_JSON_APPEND:
MCP230xx_Show(1);
break;
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_29 == XdrvMailbox.index) {
result = MCP230xx_Command();
}

View File

@ -426,14 +426,14 @@ void HandleHxAction(void)
return;
}
WSContentStart(FPSTR(D_CONFIGURE_HX711));
WSContentStart_P(S_CONFIGURE_HX711);
WSContentSendStyle();
dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1);
char stemp2[20];
dtostrfd((float)Settings.weight_item / 10000, 4, stemp2);
WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2);
WSContentSend(FPSTR(HTTP_FORM_END));
WSContentSend(FPSTR(HTTP_BTN_CONF));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentEnd();
}
@ -476,7 +476,7 @@ bool Xsns34(uint8_t function)
case FUNC_EVERY_100_MSECOND:
HxEvery100mSecond();
break;
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_34 == XdrvMailbox.index) {
result = HxCommand();
}
@ -490,10 +490,10 @@ bool Xsns34(uint8_t function)
break;
#ifdef USE_HX711_GUI
case FUNC_WEB_ADD_MAIN_BUTTON:
WSContentSend(FPSTR(HTTP_BTN_MENU_MAIN_HX711));
WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711);
break;
case FUNC_WEB_ADD_BUTTON:
WSContentSend(FPSTR(HTTP_BTN_MENU_HX711));
WSContentSend_P(HTTP_BTN_MENU_HX711);
break;
case FUNC_WEB_ADD_HANDLER:
WebServer->on("/" WEB_HANDLE_HX711, HandleHxAction);

View File

@ -613,7 +613,7 @@ bool Xsns36(uint8_t function)
case FUNC_EVERY_50_MSECOND:
MGC3130_loop();
break;
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_36 == XdrvMailbox.index) {
result = MGC3130CommandSensor();
}

View File

@ -593,7 +593,7 @@ bool Xsns40(uint8_t function)
case FUNC_EVERY_SECOND:
break;
#ifdef USE_PN532_DATA_FUNCTION
case FUNC_COMMAND:
case FUNC_COMMAND_SENSOR:
if (XSNS_40 == XdrvMailbox.index) {
result = PN532_Command();
}

494
sonoff/xsns_42_scd30.ino Normal file
View File

@ -0,0 +1,494 @@
/*
xsns_42_scd30.ino - SC30 CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2019 Frogmore42
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_I2C
#ifdef USE_SCD30
#define XSNS_42 42
#define SCD30_MAX_MISSED_READS 3
#define SONOFF_SCD30_STATE_NO_ERROR 0
#define SONOFF_SCD30_STATE_ERROR_DATA_CRC 1
#define SONOFF_SCD30_STATE_ERROR_READ_MEAS 2
#define SONOFF_SCD30_STATE_ERROR_SOFT_RESET 3
#define SONOFF_SCD30_STATE_ERROR_I2C_RESET 4
#define SONOFF_SCD30_STATE_ERROR_UNKNOWN 5
#include "Arduino.h"
#include <FrogmoreScd30.h>
#define D_CMND_SCD30 "SCD30"
const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}";
const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}";
const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}";
const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff";
/*********************************************************************************************\
* enumerationsines
\*********************************************************************************************/
enum SCD30_Commands { // commands useable in console or rules
CMND_SCD30_ALTITUDE,
CMND_SCD30_AUTOMODE,
CMND_SCD30_CALIBRATE,
CMND_SCD30_FW,
CMND_SCD30_INTERVAL,
CMND_SCD30_PRESSURE,
CMND_SCD30_TEMPOFFSET
};
FrogmoreScd30 scd30;
bool scd30Found = false;
bool scd30IsDataValid = false;
int scd30ErrorState = SONOFF_SCD30_STATE_NO_ERROR;
uint16_t scd30Interval_sec;
int scd30Loop_count = 0;
int scd30DataNotAvailable_count = 0;
int scd30GoodMeas_count = 0;
int scd30Reset_count = 0;
int scd30CrcError_count = 0;
int scd30Co2Zero_count = 0;
int i2cReset_count = 0;
uint16_t scd30_CO2 = 0;
uint16_t scd30_CO2EAvg = 0;
float scd30_Humid = 0.0;
float scd30_Temp = 0.0;
bool Scd30Init()
{
int error;
bool i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99));
if (i2c_flg)
{
uint8_t major = 0;
uint8_t minor = 0;
uint16_t interval_sec;
scd30.begin();
error = scd30.getFirmwareVersion(&major, &minor);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: did not find an SCD30: 0x%lX", error);
AddLog(LOG_LEVEL_DEBUG);
#endif
return false;
}
else
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: found an SCD30: FW v%d.%d", major, minor);
AddLog(LOG_LEVEL_INFO);
#endif
}
error = scd30.getMeasurementInterval(&scd30Interval_sec);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: error getMeasurementInterval: 0x%lX", error);
AddLog(LOG_LEVEL_ERROR);
#endif
return false;
}
error = scd30.beginMeasuring();
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "Error: Scd30BeginMeasuring: 0x%lX", error);
AddLog(LOG_LEVEL_ERROR);
#endif
return false;
}
return true;
}
}
// gets data from the sensor every 3 seconds or so to give the sensor time to gather new data
int Scd30Update()
{
int error = 0;
int16_t delta = 0;
scd30Loop_count++;
if (!scd30Found)
{
scd30Found = Scd30Init();
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "Scd30Update: found: %d ", scd30Found);
AddLog(LOG_LEVEL_INFO);
#endif
if (!scd30Found)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "Scd30Update: found: %d ", scd30Found);
AddLog(LOG_LEVEL_INFO);
#endif
return (ERROR_SCD30_NOT_FOUND_ERROR);
}
}
else
{
if (scd30Loop_count > (scd30Interval_sec - 1))
{
switch (scd30ErrorState)
{
case SONOFF_SCD30_STATE_NO_ERROR:
{
error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid);
switch (error)
{
case ERROR_SCD30_NO_ERROR:
scd30Loop_count = 0;
scd30IsDataValid = true;
scd30GoodMeas_count++;
break;
case ERROR_SCD30_NO_DATA:
scd30DataNotAvailable_count++;
break;
case ERROR_SCD30_CRC_ERROR:
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_DATA_CRC;
scd30CrcError_count++;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld", scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count);
AddLog(LOG_LEVEL_ERROR);
#endif
break;
case ERROR_SCD30_CO2_ZERO:
scd30Co2Zero_count++;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld", scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count);
AddLog(LOG_LEVEL_ERROR);
#endif
break;
default:
{
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_READ_MEAS;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld", error, scd30Loop_count);
AddLog(LOG_LEVEL_ERROR);
#endif
return (error);
}
break;
}
}
break;
case SONOFF_SCD30_STATE_ERROR_DATA_CRC:
{
//scd30IsDataValid = false;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count);
AddLog(LOG_LEVEL_ERROR);
snprintf_P(log_data, sizeof(log_data), "SCD30: got CRC error, try again, counter: %ld", scd30Loop_count);
AddLog(LOG_LEVEL_ERROR);
#endif
scd30ErrorState = ERROR_SCD30_NO_ERROR;
}
break;
case SONOFF_SCD30_STATE_ERROR_READ_MEAS:
{
//scd30IsDataValid = false;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count);
AddLog(LOG_LEVEL_ERROR);
snprintf_P(log_data, sizeof(log_data), "SCD30: not answering, sending soft reset, counter: %ld", scd30Loop_count);
AddLog(LOG_LEVEL_ERROR);
#endif
scd30Reset_count++;
error = scd30.softReset();
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: resetting got error: 0x%lX", error);
AddLog(LOG_LEVEL_ERROR);
#endif
error >>= 8;
if (error == 4)
{
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_SOFT_RESET;
}
else
{
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_UNKNOWN;
}
}
else
{
scd30ErrorState = ERROR_SCD30_NO_ERROR;
}
}
break;
case SONOFF_SCD30_STATE_ERROR_SOFT_RESET:
{
//scd30IsDataValid = false;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count);
AddLog(LOG_LEVEL_ERROR);
snprintf_P(log_data, sizeof(log_data), "SCD30: clearing i2c bus");
AddLog(LOG_LEVEL_ERROR);
#endif
i2cReset_count++;
error = scd30.clearI2CBus();
if (error)
{
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_I2C_RESET;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: error clearing i2c bus: 0x%lX", error);
AddLog(LOG_LEVEL_ERROR);
#endif
}
else
{
scd30ErrorState = ERROR_SCD30_NO_ERROR;
}
}
break;
default:
{
//scd30IsDataValid = false;
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: unknown error state: 0x%lX", scd30ErrorState);
AddLog(LOG_LEVEL_ERROR);
#endif
scd30ErrorState = SONOFF_SCD30_STATE_ERROR_SOFT_RESET; // try again
}
}
if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec))
{
scd30IsDataValid = false;
}
}
}
return (ERROR_SCD30_NO_ERROR);
}
int Scd30GetCommand(int command_code, uint16_t *pvalue)
{
switch (command_code)
{
case CMND_SCD30_ALTITUDE:
return scd30.getAltitudeCompensation(pvalue);
break;
case CMND_SCD30_AUTOMODE:
return scd30.getCalibrationType(pvalue);
break;
case CMND_SCD30_CALIBRATE:
return scd30.getForcedRecalibrationFactor(pvalue);
break;
case CMND_SCD30_INTERVAL:
return scd30.getMeasurementInterval(pvalue);
break;
case CMND_SCD30_PRESSURE:
return scd30.getAmbientPressure(pvalue);
break;
case CMND_SCD30_TEMPOFFSET:
return scd30.getTemperatureOffset(pvalue);
break;
default:
// else for Unknown command
break;
}
}
int Scd30SetCommand(int command_code, uint16_t value)
{
switch (command_code)
{
case CMND_SCD30_ALTITUDE:
return scd30.setAltitudeCompensation(value);
break;
case CMND_SCD30_AUTOMODE:
return scd30.setCalibrationType(value);
break;
case CMND_SCD30_CALIBRATE:
return scd30.setForcedRecalibrationFactor(value);
break;
case CMND_SCD30_INTERVAL:
{
int error = scd30.setMeasurementInterval(value);
if (!error)
{
scd30Interval_sec = value;
}
return error;
}
break;
case CMND_SCD30_PRESSURE:
return scd30.setAmbientPressure(value);
break;
case CMND_SCD30_TEMPOFFSET:
return scd30.setTemperatureOffset(value);
break;
default:
// else for Unknown command
break;
}
}
/*********************************************************************************************\
* Command Sensor92
\*********************************************************************************************/
bool Scd30CommandSensor()
{
char command[CMDSZ];
bool serviced = true;
uint8_t prefix_len = strlen(D_CMND_SCD30);
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { // prefix
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands);
switch (command_code) {
case CMND_SCD30_ALTITUDE:
case CMND_SCD30_AUTOMODE:
case CMND_SCD30_CALIBRATE:
case CMND_SCD30_INTERVAL:
case CMND_SCD30_PRESSURE:
case CMND_SCD30_TEMPOFFSET:
{
uint16_t value = 0;
if (XdrvMailbox.data_len > 0)
{
value = XdrvMailbox.payload16;
Scd30SetCommand(command_code, value);
}
else
{
Scd30GetCommand(command_code, &value);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SCD30_COMMAND_NVALUE, command, value);
}
break;
case CMND_SCD30_FW:
{
uint8_t major = 0;
uint8_t minor = 0;
int error;
error = scd30.getFirmwareVersion(&major, &minor);
if (error)
{
#ifdef SCD30_DEBUG
snprintf_P(log_data, sizeof(log_data), "SCD30: error getting FW version: 0x%lX", error);
AddLog(LOG_LEVEL_ERROR);
#endif
serviced = false;
}
else
{
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor);
}
}
break;
default:
// else for Unknown command
serviced = false;
break;
}
}
return serviced;
}
void Scd30Show(bool json)
{
char humidity[10];
char temperature[10];
if (scd30Found && scd30IsDataValid)
{
dtostrfd(scd30_Humid, Settings.flag2.humidity_resolution, humidity);
dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature);
if (json) {
//snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), mqtt_data, scd30_CO2, temperature, humidity);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), mqtt_data, scd30_CO2, scd30_CO2EAvg, temperature, humidity);
#ifdef USE_DOMOTICZ
if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, scd30_CO2);
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2EAVG, mqtt_data, "SCD30", scd30_CO2EAvg);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, "SCD30", scd30_CO2);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, "SCD30", temperature, TempUnit());
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, "SCD30", humidity);
#endif // USE_WEBSERVER
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns42(byte function)
{
bool result = false;
if (i2c_flg) {
switch (function) {
case FUNC_EVERY_SECOND:
Scd30Update();
break;
case FUNC_COMMAND:
result = Scd30CommandSensor();
break;
case FUNC_JSON_APPEND:
Scd30Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_APPEND:
Scd30Show(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_SCD30
#endif // USE_I2C

View File

@ -310,7 +310,9 @@ bool XsnsCall(uint8_t Function)
}
#endif // PROFILE_XSNS_SENSOR_EVERY_SECOND
if (result && (FUNC_COMMAND == Function)) {
if (result && ((FUNC_COMMAND == Function) ||
(FUNC_COMMAND_SENSOR == Function)
)) {
break;
}
#ifdef USE_DEBUG_DRIVER

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
VER = '2.1.0021'
VER = '2.1.0022'
"""
decode-config.py - Backup/Restore Sonoff-Tasmota configuration data
@ -873,7 +873,13 @@ Setting_6_4_1_16.update({
Setting_6_4_1_17 = copy.deepcopy(Setting_6_4_1_16)
Setting_6_4_1_17['flag3'][0].pop('no_pullup',None)
# ======================================================================
Setting_6_4_1_18 = copy.deepcopy(Setting_6_4_1_17)
Setting_6_4_1_18['flag3'][0].update ({
'no_hold_retain': ('<L', (0x3A0,1,12), (None, None, ('SetOption', '"SetOption62 {}".format($)')) ),
})
# ======================================================================
Settings = [
(0x6040112, 0xe00, Setting_6_4_1_18),
(0x6040111, 0xe00, Setting_6_4_1_17),
(0x6040110, 0xe00, Setting_6_4_1_16),
(0x604010D, 0xe00, Setting_6_4_1_13),

0
tools/decode-status.py Normal file → Executable file
View File