diff --git a/examples/3prog1/Makefile.am b/examples/3prog1/Makefile.am index 6cdbe82..528888d 100644 --- a/examples/3prog1/Makefile.am +++ b/examples/3prog1/Makefile.am @@ -23,11 +23,12 @@ endif AM_CFLAGS = -std=c11 -flto AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/modules -LDADD = $(top_builddir)/src/libesp32basic.a $(top_builddir)/modules/libesp32modules.a +LDADD = $(top_builddir)/modules/libesp32modules.a $(top_builddir)/src/libesp32basic.a bin_PROGRAMS = \ prog.elf + if WITH_BINARIES CLEANFILES = \ prog.bin diff --git a/examples/3prog1/README.md b/examples/3prog1/README.md index 511c036..b397699 100644 --- a/examples/3prog1/README.md +++ b/examples/3prog1/README.md @@ -13,6 +13,7 @@ A simple pattern is being written to the OLED display from left to right. * I2C_SLAVE#1: BME280 temperature/pressure/humidity sensor * I2C_SLAVE#2: BH1750FVI light sensor * I2C_SLAVE#3: 32x128 OLED display +* I2C_SLAVE#4: SHT40 Temp/hum sensor * [NODE0 .. NODE4]: 5 nodes (maybe on breadboard) as connection nodes of multiple wires. #### Connections @@ -44,4 +45,9 @@ OLED.GND -- NODE4 OLED.VCC -- NODE3 OLED.SCL -- NODE1 OLED.SDA -- NODE2 + +SHT40.GND -- NODE4 +SHT40.VIN -- NODE3 +SHT40.SCL -- NODE1 +SHT40.SDA -- NODE2 ``` diff --git a/examples/3prog1/prog.c b/examples/3prog1/prog.c index f4279c1..9788eda 100644 --- a/examples/3prog1/prog.c +++ b/examples/3prog1/prog.c @@ -28,6 +28,7 @@ #include "typeaux.h" #include "bme280.h" #include "bh1750.h" +#include "sht4x.h" #include "ssd1306.h" #include "utils/i2cutils.h" #include "utils/uartutils.h" @@ -39,6 +40,7 @@ #define OLED_PERIOD_MS 100U #define BH1750_PERIOD_MS 1333U #define BME280_PERIOD_MS 5200U +#define SHT40_PERIOD_MS 4800U #define LOG_PERIOD_MS 4000U #define INC_PERIOD_MS 1900U #define I2CSCAN_PERIOD_MS 8600U @@ -62,6 +64,9 @@ #define BME280_I2C_CH I2C1 #define BME280_I2C_SLAVEADDR 0x76 +#define SHT40_I2C_CH I2C1 +#define SHT40_I2C_SLAVEADDR 0x44 + // #3: Sizes #define UART0_TXSIZE 1U #define I2CSCAN_PRINT_PER_ROW 8 @@ -117,6 +122,8 @@ static void _bh1750_cycle(uint64_t u64tckNow); static void _bme280_init(SBme280StateDesc *psState, SI2cIfaceCfg *psIface); static void _bme280_print_result(uint64_t u64tckNow, const SBme280TPH *psRes, uint32_t u32TFine); static void _bme280_cycle(uint64_t u64tckNow); +static void _sht40_print_result(uint64_t u64tckNow, SSht40StateDesc *psState); +static void _sht40_cycle(uint64_t u64tckNow); static void _log_cycle(uint64_t u64tckNow); static void _inc_cycle(uint64_t u64tckNow); static void _uartctrl_cycle(uint64_t u64tckNow); @@ -205,12 +212,7 @@ static void _init_drivers() { static void _init_uart() { gpsUART0->CLKDIV.raw = UART_HZ2CLKDIV(UART_FREQ_HZ, APB_FREQ_HZ); - - Reg rUartMemConf = gpsUART0->MEM_CONF; - rUartMemConf &= ~(0xf << 7); - rUartMemConf |= UART0_TXSIZE << 7; - - gpsUART0->MEM_CONF = rUartMemConf; + uart_set_memconf_xsize(gpsUART0, true, UART0_TXSIZE); } // TODO: make it an ISR and attach to I2C INT @@ -384,6 +386,71 @@ static void _bme280_cycle(uint64_t u64tckNow) { } } +// Section SHT40 + +static void _sht40_print_result(uint64_t u64tckNow, SSht40StateDesc *psState) { + _uart_print_header(gpsUART0, u64tckNow, "SHT40"); + uart_printf(gpsUART0, " cmd: #%u, raw: %02X %02X %02X %02X %02X %02X,", + psState->eCommand, + psState->au8RxBuffer[0], psState->au8RxBuffer[1], psState->au8RxBuffer[2], + psState->au8RxBuffer[3], psState->au8RxBuffer[4], psState->au8RxBuffer[5]); + if (psState->eCommand != SHT4X_CMD_SERIAL) { + int32_t i32TempM = sht4x_get_temp(psState); + int32_t i32HumM = sht4x_get_hum(psState); + uart_printf(gpsUART0, " Temp: %d.%03d, Hum: %d.%03d\r\n", i32TempM / 1000, i32TempM % 1000, i32HumM / 1000, i32HumM % 1000); + } else { + uint32_t u32Serial = sht4x_get_serial(psState); + uart_printf(gpsUART0, " Serial number: %08X\r\n", u32Serial); + } + // check crc + if (!sht4x_check_crc(psState, true)) { + uart_printf(gpsUART0, " 1st CRC8 does not match (Temp)\r\n"); + } + if (!sht4x_check_crc(psState, true)) { + uart_printf(gpsUART0, " 2nd CRC8 does not match (Hum)\r\n"); + } +} + +static void _sht40_cycle(uint64_t u64tckNow) { + static ESht4xCommand eCommand = SHT4X_CMD_MEAS_H; + static uint64_t u64tckNext = MS2TICKS(SHT40_PERIOD_MS); + static bool bFirstRun = true; + static SSht40StateDesc sState; + + if (bFirstRun) { + sState = sht40_init_descriptor((SI2cIfaceCfg){SHT40_I2C_CH, SHT40_I2C_SLAVEADDR, _i2c_to_lock(SHT40_I2C_CH)}); + bFirstRun = false; + } + + uint32_t u32msWait = 0; + + if (u64tckNext <= u64tckNow) { + if (sState.eState == SHT4X_STATE_READY || sState.eState == SHT4X_STATE_ERROR) { + if (sState.eState == SHT4X_STATE_ERROR) { + _uart_print_header(gpsUART0, u64tckNow, "SHT40"); + uart_printf(gpsUART0, " ERROR!"); + } else { + if (sState.eCommand != SHT4X_CMD_RESET) { + _sht40_print_result(u64tckNow, &sState); + } + } + u32msWait = SHT40_PERIOD_MS; + ++eCommand; + if (SHT4X_CMD_RESET < eCommand) { // here we skip SHT4X_CMD_HEAT* commands + eCommand = SHT4X_CMD_MEAS_H; + } + sState.eState = SHT4X_STATE_IDLE; + } else if (sState.eState == SHT4X_STATE_IDLE) { + sht40_set_command(&sState, eCommand); + } + + if (sht40_needs_rxtx(&sState)) { + u32msWait = sht4x_rxtx_cycle(&sState); + } + u64tckNext += MS2TICKS(u32msWait); + } +} + // Section BH1750FVI static void _bh1750_init(SBh1750StateDesc *psState, SI2cIfaceCfg *psIface) { @@ -408,7 +475,7 @@ static void _bh1750_print_result(uint64_t u64tckTimestamp, const SBh1750StateDes _uart_print_header(gpsUART0, u64tckTimestamp, "BH1750"); uart_printf(gpsUART0, " mode: %s, result: %d.%03d lx (raw: %u), mtime: %u ms (raw: %u)\r\n", acBh1750MResName[eMRes], - u32mLx/1000, u32mLx%1000, u16Result, + u32mLx / 1000, u32mLx % 1000, u16Result, u32hmsMTime / 2, u8MTime); } @@ -568,7 +635,7 @@ static void _uartctrl_cycle(uint64_t u64tckNow) { if (0 == u8WaitForArgs) { // all the required number of arguments arrived switch (cCommand) { case 'w': // put a byte into lockmgr RX buffer - uint8_t u8ArgValue= char_to_hex8(acArg[0]) | (char_to_hex8(acArg[1]) << 4); + uint8_t u8ArgValue = char_to_hex8(acArg[0]) | (char_to_hex8(acArg[1]) << 4); ELockmgrResource eRes = _i2c_to_lock(OLED_I2C_CH); bool bLocked = lockmgr_is_locked(eRes); if (bLocked) { @@ -610,7 +677,7 @@ static void _uartctrl_cycle(uint64_t u64tckNow) { } uart_printf(gpsUART0, "\r\n"); } - break; + break; case 'r': { _uart_print_header(gpsUART0, u64tckNow, "CTRL"); @@ -624,7 +691,7 @@ static void _uartctrl_cycle(uint64_t u64tckNow) { lockmgr_free_lock(eRes); } } - break; + break; case 'w': cCommand = 'w'; u8WaitForArgs = 2; @@ -675,5 +742,6 @@ void prog_cycle_pro(uint64_t u64tckNow) { _i2cscan_cycle(u64tckNow); _bh1750_cycle(u64tckNow); _bme280_cycle(u64tckNow); + _sht40_cycle(u64tckNow); _uartctrl_cycle(u64tckNow); } diff --git a/modules/Makefile.am b/modules/Makefile.am index 99e8ffc..b0b6e1b 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -5,10 +5,10 @@ libesp32modules_a_AR=$(AR) rcs lib_LIBRARIES = libesp32modules.a -include_HEADERS = bh1750.h bme280.h dht22.h ssd1306.h tm1637.h ws2812.h +include_HEADERS = bh1750.h bme280.h dht22.h sht4x.h ssd1306.h tm1637.h ws2812.h nodist_include_HEADERS = -libesp32modules_a_SOURCES = bh1750.c bme280.c dht22.c ssd1306.c tm1637.c ws2812.c +libesp32modules_a_SOURCES = bh1750.c bme280.c dht22.c sht4x.c ssd1306.c tm1637.c ws2812.c nodist_libesp32modules_a_SOURCES = CLEANFILES = diff --git a/modules/sht4x.c b/modules/sht4x.c new file mode 100644 index 0000000..ce97b52 --- /dev/null +++ b/modules/sht4x.c @@ -0,0 +1,187 @@ +/* + * Copyright 2024 - 2025 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#include "i2c.h" +#include "sht4x.h" +#include "utils/crc.h" + +// ============== Defines ============== + +// ============== Local types ============== + +// ============== Local data ============== +static const uint8_t gau8Command[] = { + 0xFD, 0xF6, 0xE0, + 0x89, 0x94, + 0x39, 0x32, + 0x2F, 0x24, + 0x1E, 0x15 +}; + +static const uint32_t gau32msWait[] = { + 9, 5, 2, + 1, 1, + 1100, 110, + 1100, 110, + 1100, 110 +}; +// ============== Internal function declarations ============== +static inline bool _command_has_data_response(ESht4xCommand eCommand); +static inline uint16_t _pu8_to_u16(const uint8_t *pu8Data); + +// ============== Implementation ============== +// -------------- Internal functions -------------- +/** + * Tells whether a given command produces data to be read or not. + * @param eCommand Given command. + * @return The command produces data to be read. + */ +static inline bool _command_has_data_response(ESht4xCommand eCommand) { + return eCommand != SHT4X_CMD_RESET; +} + +/** + * Converts the first two bytes of a (big endian) data stream to uint16_t. + * @param pu8Data Input data stream with at least 2 bytes. + * @return The first two bytes converted into uint16_t type. + */ +static inline uint16_t _pu8_to_u16(const uint8_t *pu8Data) { + return (pu8Data[0] << 8) | (pu8Data[1] & 0xFF); +} + +// -------------- Interface functions -------------- +/** + * Get raw temperature data. + * Valid only in SHT4X_STATE_READY communication state + * and for SHT4X_CMD_MEAS_* or SHT4X_CMD_HEAT* commands. + * @param psState Current state. + * @return Raw temperature data. + */ +uint16_t sht4x_get_temp_raw(const SSht40StateDesc *psState) { + return _pu8_to_u16(psState->au8RxBuffer); +} + +/** + * Get raw humidity data. + * Valid only in SHT4X_STATE_READY communication state + * and for SHT4X_CMD_MEAS_* or SHT4X_CMD_HEAT* commands. + * @param psState Current state. + * @return Raw humidity data. + */ +uint16_t sht4x_get_hum_raw(const SSht40StateDesc *psState) { + return _pu8_to_u16(psState->au8RxBuffer + 3); +} + +/** + * Get temperature value in 0.001°C resolution. + * Valid only in SHT4X_STATE_READY communication state + * and for SHT4X_CMD_MEAS_* or SHT4X_CMD_HEAT* commands. + * @param psState Current state. + * @return Temperature data in 0.001°C resolution. + */ +int32_t sht4x_get_temp(const SSht40StateDesc *psState) { + return ((sht4x_get_temp_raw(psState) * 2734) >> 10) - 45000; +} + +/** + * Get relative humidity data in 0.001% resolution. + * Valid only in SHT4X_STATE_READY communication state + * and for SHT4X_CMD_MEAS_* or SHT4X_CMD_HEAT* commands. + * @param psState Current state. + * @return Humidity data in 0.001% resolution. + */ +int32_t sht4x_get_hum(const SSht40StateDesc *psState) { + return ((sht4x_get_hum_raw(psState) * 1953) >> 10) - 6000; +} + +/** + * Get serial number. + * Valid only in SHT4X_STATE_READY communication state + * and for SHT4X_CMD_SERIAL command. + * @param psState + * @return Serial number. + */ +uint32_t sht4x_get_serial(const SSht40StateDesc *psState) { + return (_pu8_to_u16(psState->au8RxBuffer) << 16) | _pu8_to_u16(psState->au8RxBuffer + 3); +} + +/** + * SHT4x devices send data with CRC checksum. + * The 6 byte long responses are in format: + * (1st data byte, 2nd data byte, checksum of the first data byte pair, + * 3rd data byte, 4th data byte, checksum of the second data byte pair). + * This function checks whether the a checksum is OK. + * Valid only in SHT4X_STATE_READY communication state + * and for any command except for SHT4X_CMD_RESET (reset does not produces any data response). + * @param psState Current state. + * @param bFirst True check the CRC of the first data byte pair, false: check the CRC of the second data pair. + * @return CRC byte is OK. + */ +bool sht4x_check_crc(const SSht40StateDesc *psState, bool bFirst) { + const uint8_t *pu8Data = psState->au8RxBuffer + (bFirst ? 0 : 3); + uint8_t u8CrcRecv = psState->au8RxBuffer[bFirst ? 2 : 5]; + + uint8_t u8CrcCalc = crc8(SHT40_CRC_POLY, SHT40_CRC_INIT, pu8Data, 2); + return (u8CrcRecv == u8CrcCalc); +} + +/** + * This function is responsible for I2C communicating to SHT4x device. + * It must be called multiple times once the a command is issued, until + * SHT4X_STATE_READY or SHT4X_STATE_ERROR is reached. + * It returns a hint saying when the next calling should be done (in ms). + * @param psState Current state. + * @return After how many ms the function should be called again. + */ +uint32_t sht4x_rxtx_cycle(SSht40StateDesc *psState) { + uint32_t u32msWait = 0U; + + // recv side + if (psState->eState == SHT4X_STATE_RD_RECV || psState->eState == SHT4X_STATE_WR_RECV) { + AsyncResultEntry *psEntry = lockmgr_get_entry(psState->u32LockLabel); + if (psEntry->bReady) { + bool bErr = (0 < (psEntry->u32IntSt & I2C_INT_MASK_ERR)); + if (!bErr) { + if (_command_has_data_response(psState->eCommand)) { + ++psState->eState; + } else { + psState->eState = SHT4X_STATE_READY; + } + } else { + // maybe repeat corresponding SEND + psState->eState = SHT4X_STATE_ERROR; + } + lockmgr_release_entry(psState->u32LockLabel); + } else { + // I2C is still not ready.. maybe wait more time + u32msWait = 1; + } + } + + // send side + if (psState->eState == SHT4X_STATE_RD_SEND || psState->eState == SHT4X_STATE_WR_SEND) { + if (lockmgr_acquire_lock(psState->sIface.eLck, &psState->u32LockLabel)) { + switch (psState->eState) { + case SHT4X_STATE_WR_SEND: + i2c_write(psState->sIface.eBus, psState->sIface.u8SlaveAddr, 1, &gau8Command[psState->eCommand]); + u32msWait = gau32msWait[psState->eCommand]; + break; + case SHT4X_STATE_RD_SEND: // read data + AsyncResultEntry *psEntry = lockmgr_get_entry(psState->u32LockLabel); + psEntry->pu8ReceiveBuffer = psState->au8RxBuffer; + psEntry->u8RxLen = 6; + i2c_read(psState->sIface.eBus, psState->sIface.u8SlaveAddr, 6); + break; + default: + // this branch should not be reached + } + ++psState->eState; + } + } + + return u32msWait; +} diff --git a/modules/sht4x.h b/modules/sht4x.h new file mode 100644 index 0000000..32cdcdb --- /dev/null +++ b/modules/sht4x.h @@ -0,0 +1,110 @@ +/* + * Copyright 2024 - 2025 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ +#ifndef SHT4X_H +#define SHT4X_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "lockmgr.h" +#include "utils/i2ciface.h" + + // ============== Defines ============== +#define SHT40_CRC_POLY 0x31 +#define SHT40_CRC_INIT 0xFF + + // ============== Types ============== + + /** + * List of accepted SHT4X commands (documented in manual). + */ + typedef enum { + SHT4X_CMD_MEAS_H = 0, + SHT4X_CMD_MEAS_M = 1, + SHT4X_CMD_MEAS_L, + SHT4X_CMD_SERIAL, + SHT4X_CMD_RESET, + SHT4X_CMD_HEAT200MW_1S, + SHT4X_CMD_HEAT200MW_01S, + SHT4X_CMD_HEAT110MW_1S, + SHT4X_CMD_HEAT110MW_01S, + SHT4X_CMD_HEAT20MW_1S, + SHT4X_CMD_HEAT20MW_01S + } ESht4xCommand; + + /** + * Set of host -- device (ESP32 -- SHT40) communication states. + */ + typedef enum { + SHT4X_STATE_IDLE = 0, ///< Idle state, nothing to be done. + SHT4X_STATE_WR_SEND = 1, ///< WRITE command to be sent (host to device). + SHT4X_STATE_WR_RECV = 2, ///< Waiting for write command to be finished (receive signal (not busy) from peripheral controller) + SHT4X_STATE_RD_SEND = 3, ///< READ command to be sent (host to device). + SHT4X_STATE_RD_RECV = 4, ///< Waiting for read command to be finished (receive signal (not busy) from peripheral controller) + SHT4X_STATE_READY = 5, ///< Communication done, result data arrived. + SHT4X_STATE_ERROR = 6 ///< Something went wrong (probably I2C interrupt register shows error). + } ESht4xCommState; + + /** + * State descriptor of SHT4x connection. + */ + typedef struct { + ESht4xCommState eState; ///< Current communication state + ESht4xCommand eCommand; ///< Current SHT4x command. + uint32_t u32LockLabel; ///< Label used in LockManager. + uint8_t au8RxBuffer[6]; ///< Receive buffer. + SI2cIfaceCfg sIface; /// I2C communication interface. + } SSht40StateDesc; + + // ============== Global values / References ============== + + // ============== Inline interface functions ============== + + static inline SSht40StateDesc sht40_init_descriptor(SI2cIfaceCfg sIface) { + return (SSht40StateDesc){.eState = SHT4X_STATE_IDLE, .sIface = sIface}; + } + + /** + * In idle state sets the SHT4X command that will be sent to the device (by calling sht4x_comm_cycle_ms()). + * @param psState + * @param eCommand + * @return + */ + static inline bool sht40_set_command(SSht40StateDesc *psState, ESht4xCommand eCommand) { + if (psState->eState != SHT4X_STATE_IDLE) return false; + psState->eCommand = eCommand; + psState->eState = SHT4X_STATE_WR_SEND; + return true; + } + + /** + * Tells whether in current state I2C interaction (i.e., calling sht4x_comm_cycle_ms()) is needed + * @param psState Current state + * @return true: sht4x_comm_cycle_ms() should be called. + */ + static inline bool sht40_needs_rxtx(const SSht40StateDesc *psState) { + return SHT4X_STATE_WR_SEND <= psState->eState && psState->eState <= SHT4X_STATE_RD_RECV; + } + + // ============== Interface functions ============== + uint16_t sht4x_get_temp_raw(const SSht40StateDesc *psState); + uint16_t sht4x_get_hum_raw(const SSht40StateDesc *psState); + int32_t sht4x_get_temp(const SSht40StateDesc *psState); + int32_t sht4x_get_hum(const SSht40StateDesc *psState); + uint32_t sht4x_get_serial(const SSht40StateDesc *psState); + bool sht4x_check_crc(const SSht40StateDesc *psState, bool bFirst); + + uint32_t sht4x_rxtx_cycle(SSht40StateDesc *psState); +#ifdef __cplusplus +} +#endif + +#endif /* SHT4X_H */ + diff --git a/src/Makefile.am b/src/Makefile.am index 47db425..9f8f543 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,10 +7,10 @@ lib_LIBRARIES = libesp32basic.a include_HEADERS = dport.h esp32types.h esp_attr.h gpio.h i2c.h \ iomux.h lockmgr.h main.h pidctrl.h print.h rmt.h romfunctions.h rtc.h timg.h \ typeaux.h uart.h xtutils.h \ - utils/i2cutils.h utils/i2ciface.h utils/rmtutils.h utils/uartutils.h utils/generators.h + utils/crc.h utils/i2cutils.h utils/i2ciface.h utils/rmtutils.h utils/uartutils.h utils/generators.h nodist_include_HEADERS = -libesp32basic_a_SOURCES = i2c.c lockmgr.c main.c rmt.c timg.c uart.c utils/i2cutils.c utils/rmtutils.c utils/uartutils.c utils/generators.c +libesp32basic_a_SOURCES = i2c.c lockmgr.c main.c rmt.c timg.c uart.c utils/crc.c utils/i2cutils.c utils/rmtutils.c utils/uartutils.c utils/generators.c nodist_libesp32basic_a_SOURCES = CLEANFILES = diff --git a/src/utils/crc.c b/src/utils/crc.c new file mode 100644 index 0000000..e2b2186 --- /dev/null +++ b/src/utils/crc.c @@ -0,0 +1,44 @@ +/* + * Copyright 2024 - 2025 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#include "crc.h" + +/** + * Calculates CRC8 value of a single byte. + * @param u8Poly CRC polynomial. + * @param u8Input Input value. + * @return CRC8 of u8Input. + */ +uint8_t crc8byte(uint8_t u8Poly, uint8_t u8Input) { + static const uint32_t u32PadMask = 0xFF; + uint32_t u32Input = u8Input << 8; + uint32_t u32Divisor = u8Poly | 0x100; + while (u32Divisor < u32Input) u32Divisor <<= 1; + while (u32PadMask < u32Input) { + if ((u32Input ^ u32Divisor) < u32Input) { + u32Input ^= u32Divisor; + } + u32Divisor >>= 1; + } + return (uint8_t) u32Input; +} + +/** + * Calculates the CRC8 value of a bytestream. + * @param u8Poly CRC polynomial. + * @param u8Init Inital CRC value + * @param pu8Message Input bytestream. + * @param szMessageLen Length of the input. + * @return CRC8 of the bytestream. + */ +uint8_t crc8(uint8_t u8Poly, uint8_t u8Init, const uint8_t *pu8Message, size_t szMessageLen) { + uint8_t u8Crc = u8Init; + for (size_t i = 0; i < szMessageLen; ++i) { + u8Crc = crc8byte(u8Poly, u8Crc ^ pu8Message[i]); + } + return u8Crc; +} diff --git a/src/utils/crc.h b/src/utils/crc.h new file mode 100644 index 0000000..3f13fae --- /dev/null +++ b/src/utils/crc.h @@ -0,0 +1,26 @@ +/* + * Copyright 2024 - 2025 SZIGETI János + * + * This file is part of Bilis ESP32 Basic, which is released under GNU General Public License.version 3. + * See LICENSE or for full license details. + */ + +#ifndef CRC_H +#define CRC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + uint8_t crc8byte(uint8_t u8Poly, uint8_t u8Input); + uint8_t crc8(uint8_t u8Poly, uint8_t u8Init, const uint8_t *pu8Message, size_t szMessageLen); + +#ifdef __cplusplus +} +#endif + +#endif /* CRC_H */ +