From 5f14fdf1e97e8d1279ee83ef9ea7b3feeb77cb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 16 Mar 2016 19:16:02 +0100 Subject: [PATCH] sbmp added but too large --- .gitmodules | 3 - Makefile | 12 +- esphttpd.pro | 27 +- libesphttpd/Makefile | 8 +- sbmp/README.md | 110 ++++++++ sbmp/crc32.c | 144 +++++++++++ sbmp/crc32.h | 51 ++++ sbmp/sbmp.h | 23 ++ sbmp/sbmp_checksum.c | 105 ++++++++ sbmp/sbmp_checksum.h | 39 +++ sbmp/sbmp_config.h | 53 ++++ sbmp/sbmp_datagram.c | 69 +++++ sbmp/sbmp_datagram.h | 71 +++++ sbmp/sbmp_frame.c | 442 ++++++++++++++++++++++++++++++++ sbmp/sbmp_frame.h | 205 +++++++++++++++ sbmp/sbmp_logging.c | 6 + sbmp/sbmp_logging.h | 15 ++ sbmp/sbmp_session.c | 416 ++++++++++++++++++++++++++++++ sbmp/sbmp_session.h | 207 +++++++++++++++ user/datalink.c | 27 ++ user/datalink.h | 6 + user/{cfg_serial.c => serial.c} | 43 ++-- user/{cfg_serial.h => serial.h} | 0 user/user_main.c | 26 +- 24 files changed, 2056 insertions(+), 52 deletions(-) delete mode 100644 .gitmodules create mode 100644 sbmp/README.md create mode 100644 sbmp/crc32.c create mode 100644 sbmp/crc32.h create mode 100644 sbmp/sbmp.h create mode 100644 sbmp/sbmp_checksum.c create mode 100644 sbmp/sbmp_checksum.h create mode 100644 sbmp/sbmp_config.h create mode 100644 sbmp/sbmp_datagram.c create mode 100644 sbmp/sbmp_datagram.h create mode 100644 sbmp/sbmp_frame.c create mode 100644 sbmp/sbmp_frame.h create mode 100644 sbmp/sbmp_logging.c create mode 100644 sbmp/sbmp_logging.h create mode 100644 sbmp/sbmp_session.c create mode 100644 sbmp/sbmp_session.h create mode 100644 user/datalink.c create mode 100644 user/datalink.h rename user/{cfg_serial.c => serial.c} (91%) rename user/{cfg_serial.h => serial.h} (100%) diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f339e54..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "libesphttpd"] - path = libesphttpd - url = http://git.spritesserver.nl/libesphttpd.git/ diff --git a/Makefile b/Makefile index d52bc58..6f4eaf9 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ APPGEN ?= $(SDK_BASE)/tools/gen_appbin.py TARGET = httpd # which modules (subdirectories) of the project to include in compiling -MODULES = user -EXTRA_INCDIR = include libesphttpd/include +MODULES = user sbmp +EXTRA_INCDIR = include libesphttpd/include sbmp # libraries used in this project, mainly provided by the SDK LIBS = c gcc hal phy pp net80211 wpa main lwip crypto @@ -184,10 +184,10 @@ endef all: checkdirs $(TARGET_OUT) $(FW_BASE) $(Q) echo -e "\nBuild OK.\n" -libesphttpd/Makefile: - $(Q) echo "No libesphttpd submodule found. Using git to fetch it..." - $(Q) git submodule init - $(Q) git submodule update +# libesphttpd/Makefile: +# $(Q) echo "No libesphttpd submodule found. Using git to fetch it..." +# $(Q) git submodule init +# $(Q) git submodule update libesphttpd: libesphttpd/Makefile $(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) diff --git a/esphttpd.pro b/esphttpd.pro index daa3e93..361d074 100644 --- a/esphttpd.pro +++ b/esphttpd.pro @@ -10,7 +10,8 @@ INCLUDEPATH = . \ include \ libesphttpd/include \ libesphttpd/espfs \ - libesphttpd/core + libesphttpd/core \ + sbmp SOURCES += \ libesphttpd/core/auth.c \ @@ -40,8 +41,15 @@ SOURCES += \ user/cgi.c \ user/io.c \ user/user_main.c \ - user/cfg_serial.c \ - user/uart_driver.c + user/uart_driver.c \ + sbmp/crc32.c \ + sbmp/sbmp_checksum.c \ + sbmp/sbmp_datagram.c \ + sbmp/sbmp_frame.c \ + sbmp/sbmp_logging.c \ + sbmp/sbmp_session.c \ + user/datalink.c \ + user/serial.c HEADERS += \ include/uart_hw.h \ @@ -73,7 +81,6 @@ HEADERS += \ user/cgi.h \ user/io.h \ user/uart_register.h \ - user/cfg_serial.h \ esp_iot_sdk_v1.5.2/include/json/json.h \ esp_iot_sdk_v1.5.2/include/json/jsonparse.h \ esp_iot_sdk_v1.5.2/include/json/jsontree.h \ @@ -102,7 +109,17 @@ HEADERS += \ esp_iot_sdk_v1.5.2/include/uart_register.h \ esp_iot_sdk_v1.5.2/include/upgrade.h \ esp_iot_sdk_v1.5.2/include/user_interface.h \ - user/uart_driver.h + user/uart_driver.h \ + sbmp/crc32.h \ + sbmp/sbmp.h \ + sbmp/sbmp_checksum.h \ + sbmp/sbmp_config.h \ + sbmp/sbmp_datagram.h \ + sbmp/sbmp_frame.h \ + sbmp/sbmp_logging.h \ + sbmp/sbmp_session.h \ + user/datalink.h \ + user/serial.h DISTFILES += \ style.astylerc \ diff --git a/libesphttpd/Makefile b/libesphttpd/Makefile index 6faa1c8..2e447de 100644 --- a/libesphttpd/Makefile +++ b/libesphttpd/Makefile @@ -139,10 +139,10 @@ endef all: checkdirs $(LIB) webpages.espfs libwebpages-espfs.a submodules: lib/heatshrink/Makefile -lib/heatshrink/Makefile: - $(Q) echo "Heatshrink isn't found. Checking out submodules to fetch it." - $(Q) git submodule init - $(Q) git submodule update +# lib/heatshrink/Makefile: +# $(Q) echo "Heatshrink isn't found. Checking out submodules to fetch it." +# $(Q) git submodule init +# $(Q) git submodule update $(LIB): $(BUILD_DIR) submodules $(OBJ) diff --git a/sbmp/README.md b/sbmp/README.md new file mode 100644 index 0000000..b1ed69b --- /dev/null +++ b/sbmp/README.md @@ -0,0 +1,110 @@ +SBMP library, v1.3 +================== + +This is a reference implementation of SBMP, which should be usable in embedded +environment. + +The library cosists of a **Framing layer**, **Datagram middleware** and a **Session layer**. + +How to use +---------- + +The framing layer is isolated in the `sbmp_frame` module, and can be used on it's own, +if sessions are not needed. That gives you reliable data transfer with error detection. + +If you want to *get the most out of SBMP*, use the session layer. Session layer functions are +namespaced `sbmp_ep_` ("ep" stands for endpoint). + +All header files are included in `sbmp.h`, which is the only file you should +`#include` in your application. + +Read comments in the examples to see how to use the library. + +Configuration & porting +----------------------- + +You can customize a lot of aspects of SBMP in `sbmp_config.h`. + +If you included the library as a submodule, and want to avoid manual edits, you can set +the options using compiler flags (eg. to disable CRC32: `-DSBMP_HAS_CRC32=0`). + +**What to change for Arduino** + +Basically disable all you can in the config file. + +- You don't want `malloc()` +- Logging is useless if there's only one USART anyway +- CRC32 can be replaced with XOR if you don't care about reliability that much. + +This gains you a huge size reduction, and the whole library will take only around +2.5 kB flash and 150 B ram (of course, not including your Rx buffer). + +It works really well for ATmega328P - the usual Arduino chip. + +Example for AVR +--------------- + +This example uses the [AVR C boilerplate](https://github.com/MightyPork/avr-c-boilerplate). + +It's here just to show how to set up SBMP in your AVR application. + +**NOTE:** This example uses static allocation of the struct and buffer. + +If you pass NULLs to the init function, it will `malloc()` it for you +and return a pointer to the Endpoint. + +```c +#include // register definitions +#include // interrupt vectors + +#include +#include + +// AVR C boilerplate libs +#include "lib/iopins.h" +#include "lib/usart.h" + +// SBMP +#include "sbmp/sbmp.h" + +// SBMP globals +#define RXBUF_LEN 128 +static uint8_t rxbuf[RXBUF_LEN]; +static SBMP_Endpoint ep; + +/** USART receive handler */ +ISR(USART_RX_vect) +{ + // Get the byte and pass it to SBMP + uint8_t b = usart_rx(); + sbmp_ep_receive(&ep, b); // WARN: This can fail. Should check retval. +} + +/** Message received from SBMP */ +void message_received(SBMP_Datagram *dg) +{ + // ... +} + +int main() +{ + usart_init(BAUD_115200); + usart_isr_rx_enable(true); // enable the interrupt + + // init SBMP + sbmp_ep_init(&ep, rxbuf, RXBUF_LEN, message_received, usart_tx); + + // ...additional SBMP configuration if needed... + + // enable rx, tx + sbmp_ep_enable(&ep, true); + + // globally enable interrupts (for the USART_RX handler) + sei(); + + while (1) { + // ... do stuff + } +} +``` + diff --git a/sbmp/crc32.c b/sbmp/crc32.c new file mode 100644 index 0000000..49bae49 --- /dev/null +++ b/sbmp/crc32.c @@ -0,0 +1,144 @@ +#include "esp8266.h" + +#include "crc32.h" +#include "sbmp_config.h" + +#if SBMP_HAS_CRC32 + +// This file was downloaded from some forum, and modified a bit. + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static uint32_t ICACHE_RODATA_ATTR STORE_ATTR crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8)) + +uint32_t crc32_begin(void) +{ + return 0xFFFFFFFF; +} + +uint32_t crc32_update(uint32_t crc_scratch, uint8_t ch) +{ + return UPDC32(ch, crc_scratch); +} + +uint32_t crc32_end(uint32_t crc_scratch) +{ + return ~crc_scratch; +} + +uint32_t crc32buf(uint8_t *buf, size_t len) +{ + uint32_t scratch = crc32_begin(); + + for (; len; --len, ++buf) { + scratch = crc32_update(scratch, *buf); + } + + return crc32_end(scratch); +} + +#endif /* SBMP_HAS_CRC32 */ diff --git a/sbmp/crc32.h b/sbmp/crc32.h new file mode 100644 index 0000000..383900e --- /dev/null +++ b/sbmp/crc32.h @@ -0,0 +1,51 @@ +#ifndef CRC32_H +#define CRC32_H + +#include "sbmp_config.h" +#if SBMP_HAS_CRC32 + +/** + * This module is used by the framing layer to calculate CRC32. + * + * If your hardware provides a hardware checksum calculator, + * you can modify the .c file to use that instead of + * doing it in software. + */ + +#include +#include +#include + +/** + * @brief Calculate CRC32 of a buffer. + * @param buf : buffer to checksum + * @param len : buffer size + * @return the CRC32 checksum + */ +uint32_t crc32buf(uint8_t *buf, size_t len); + +/** + * @brief Start calculating a checksum of a block of data. + * @return the scratch value, for use in the update and end functions. + */ +uint32_t crc32_begin(void); + +/** + * @brief Update the CRC32 scratch value with a byte of the data block. + * @param crc_scratch : old scratch value pointer. + * @param b : received byte + * @return updated scratch + */ +uint32_t crc32_update(uint32_t crc_scratch, uint8_t b); + +/** + * @brief Finish the CRC calculation. + * @param crc_scratch : your scratch buffer. + * @return the final CRC32 value. + */ +uint32_t crc32_end(uint32_t crc_scratch); + + +#endif /* SBMP_HAS_CRC32 */ + +#endif /* CRC32_H */ diff --git a/sbmp/sbmp.h b/sbmp/sbmp.h new file mode 100644 index 0000000..f3f24f5 --- /dev/null +++ b/sbmp/sbmp.h @@ -0,0 +1,23 @@ +#ifndef SBMP_H +#define SBMP_H + +/** + * This is the main SBMP header file. + * + * This is the only header file you should need to + * #include in your application code. + */ + +#include "sbmp_config.h" + +// Common utils & the frame parser +#include "sbmp_logging.h" +#include "sbmp_checksum.h" + +#include "sbmp_frame.h" + +// Datagram & session layer +#include "sbmp_datagram.h" +#include "sbmp_session.h" + +#endif /* SBMP_H */ diff --git a/sbmp/sbmp_checksum.c b/sbmp/sbmp_checksum.c new file mode 100644 index 0000000..463d6da --- /dev/null +++ b/sbmp/sbmp_checksum.c @@ -0,0 +1,105 @@ +#include "esp8266.h" + +#include + +#include "sbmp_config.h" +#include "sbmp_checksum.h" + +#if SBMP_HAS_CRC32 +#include "crc32.h" +#endif + + +/** Get nr of bytes in a checksum */ +uint8_t chksum_length(SBMP_CksumType cksum_type) +{ + switch (cksum_type) { + case SBMP_CKSUM_CRC32: return 4; + case SBMP_CKSUM_XOR: return 1; + case SBMP_CKSUM_NONE: return 0; + default: + return 4; // assume all unknown checksums are 4 bytes long + } +} + +/** Start calculating a checksum */ +void cksum_begin(SBMP_CksumType type, uint32_t *scratch) +{ + switch (type) { + +#if SBMP_HAS_CRC32 + case SBMP_CKSUM_CRC32: + *scratch = crc32_begin(); + break; +#endif + case SBMP_CKSUM_XOR: + *scratch = 0; + break; + + case SBMP_CKSUM_NONE: // fall-through + default: + ; + } +} + +/** Update the checksum calculation with an incoming byte */ +void cksum_update(SBMP_CksumType type, uint32_t *scratch, uint8_t byte) +{ + switch (type) { + +#if SBMP_HAS_CRC32 + case SBMP_CKSUM_CRC32: + *scratch = crc32_update(*scratch, byte); + break; +#endif + case SBMP_CKSUM_XOR: + *scratch ^= byte; + break; + + case SBMP_CKSUM_NONE: // fall-through + default: + ; + } +} + +/** Stop the checksum calculation, get the result */ +void cksum_end(SBMP_CksumType type, uint32_t *scratch) +{ + switch (type) { + +#if SBMP_HAS_CRC32 + case SBMP_CKSUM_CRC32: + *scratch = crc32_end(*scratch); +#endif + case SBMP_CKSUM_XOR: + // scratch already contains the checksum + break; + + case SBMP_CKSUM_NONE: // fall-through + default: + *scratch = 0; + break; + } +} + +/** Check if the calculated checksum matches the received one */ +bool cksum_verify(SBMP_CksumType type, uint32_t *scratch, uint32_t received_cksum) +{ + cksum_end(type, scratch); + + // scratch now contains the checksum + + switch (type) { + +#if SBMP_HAS_CRC32 + case SBMP_CKSUM_CRC32: // fall-through +#endif + case SBMP_CKSUM_XOR: + return (*scratch == received_cksum); + + case SBMP_CKSUM_NONE: // fall-through + default: + // unknown checksum type + return true; // assume it's OK + } +} diff --git a/sbmp/sbmp_checksum.h b/sbmp/sbmp_checksum.h new file mode 100644 index 0000000..6435608 --- /dev/null +++ b/sbmp/sbmp_checksum.h @@ -0,0 +1,39 @@ +#ifndef SBMP_CHECKSUM_H +#define SBMP_CHECKSUM_H + +/** + * Checksum functions for the SBMP framing layer. + */ + +#include +#include + +#include "sbmp_config.h" + + +/** Checksum types */ +typedef enum { + SBMP_CKSUM_NONE = 0, /*!< No checksum */ + SBMP_CKSUM_CRC32 = 32, /*!< ISO CRC-32 */ + SBMP_CKSUM_XOR = 1, /*!< Simple XOR check, good for small micros (Arduino) */ + +} SBMP_CksumType; + + +/** Get nr of bytes in a checksum */ +uint8_t chksum_length(SBMP_CksumType cksum_type); + +/** Start calculating a checksum. Updates scratch. */ +void cksum_begin(SBMP_CksumType type, uint32_t *scratch); + +/** Update the checksum calculation with an incoming byte. Updates scratch. */ +void cksum_update(SBMP_CksumType type, uint32_t *scratch, uint8_t byte); + +/** Stop the checksum calculation, get the result */ +void cksum_end(SBMP_CksumType type, uint32_t *scratch); + +/** Check if the calculated checksum matches the received one */ +bool cksum_verify(SBMP_CksumType type, uint32_t *scratch, uint32_t received_cksum); + + +#endif /* SBMP_CHECKSUM_H */ diff --git a/sbmp/sbmp_config.h b/sbmp/sbmp_config.h new file mode 100644 index 0000000..6a932dd --- /dev/null +++ b/sbmp/sbmp_config.h @@ -0,0 +1,53 @@ +#ifndef SBMP_CONFIG_H +#define SBMP_CONFIG_H + +/* --- Configuration ------------------- */ + + +#ifndef SBMP_LOGGING +/** + * @brief Enable logging. + * + * Logging functions are WEAK stubs in sbmp_logging. + * + * Disable logging to free up memory taken by the messages. + */ +#define SBMP_LOGGING 1 +#endif + + +#ifndef SBMP_MALLOC +/** + * @brief Enable malloc if NULL is passed. + * + * This lets you malloc() the struct / buffer if you pass NULL + * to the init functions. + * + * Disable malloc to free up memory taken by the malloc routine. + * If disabled, init funcs will return NULL if NULL is passed + * as argument. + */ +#define SBMP_MALLOC 1 +#endif + + +#ifndef SBMP_HAS_CRC32 +/** + * @brief Add support for CRC32 + * + * Disabling CRC32 will reduce program size (for small micros). + * If CRC32 is disabled, XOR will be used as the preferred checksum + * method. + * + * Received CRC32'd messages will be accepted without checking. + * + * If handshake is used, the peer will detect that CRC32 is not + * supported here, and should start using XOR. + */ +#define SBMP_HAS_CRC32 0 +#endif + +/* ------------------------------------- */ + + +#endif // SBMP_CONFIG_H diff --git a/sbmp/sbmp_datagram.c b/sbmp/sbmp_datagram.c new file mode 100644 index 0000000..1849624 --- /dev/null +++ b/sbmp/sbmp_datagram.c @@ -0,0 +1,69 @@ +#include "esp8266.h" + +#include "sbmp_config.h" +#include "sbmp_logging.h" +#include "sbmp_datagram.h" + +SBMP_Datagram *sbmp_dg_parse(SBMP_Datagram *dg, const uint8_t *payload, uint16_t length) +{ + if (length < 3) { + sbmp_error("Can't parse datagram, payload too short."); + return NULL; // shorter than the minimal no-payload datagram + } + + // S.N. (2 B) | Dg Type (1 B) | Payload + +#if SBMP_MALLOC + if (dg == NULL) { + // request to allocate + dg = malloc(sizeof(SBMP_Datagram)); + } +#else + if (dg == NULL) { + return NULL; // fail + } +#endif + + dg->session = (uint16_t)((payload[0]) | (payload[1] << 8)); + dg->type = payload[2]; + dg->length = length - 3; + dg->payload = payload + 3; // pointer arith + + return dg; +} + + +/** Start a datagram transmission */ +bool sbmp_dg_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t session, SBMP_DgType type, uint16_t length) +{ + if (length > (0xFFFF - 3)) { + sbmp_error("Can't send a datagram, payload too long."); + return false; + } + + if (frm->tx_status != FRM_STATE_IDLE) { + sbmp_error("Can't state datagram, SBMP tx not IDLE."); + return false; + } + + if (! sbmp_frm_start(frm, cksum_type, length + 3)) return false; + + sbmp_frm_send_byte(frm, session & 0xFF); + sbmp_frm_send_byte(frm, (session >> 8) & 0xFF); + sbmp_frm_send_byte(frm, type); + + return true; +} + + +/** Send a whole datagram in one go */ +bool sbmp_dg_send(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, SBMP_Datagram *dg) +{ + if (! sbmp_dg_start(frm, cksum_type, dg->session, dg->type, dg->length)) { + sbmp_error("Failed to start datagram."); + return false; + } + + size_t n = sbmp_frm_send_buffer(frm, dg->payload, dg->length); + return (n == dg->length); +} diff --git a/sbmp/sbmp_datagram.h b/sbmp/sbmp_datagram.h new file mode 100644 index 0000000..65a64fd --- /dev/null +++ b/sbmp/sbmp_datagram.h @@ -0,0 +1,71 @@ +#ifndef SBMP_DATAGRAM_H +#define SBMP_DATAGRAM_H + +/** + * SBMP datagram layer + * + * This layer is coupled to the framing layer, and + * builds a datagram abstraction on top of it. + * + * This layer sits right under the session layer, + * and is not very useful on it's own. + */ + +#include + +#include "sbmp_config.h" +#include "sbmp_frame.h" + +typedef uint8_t SBMP_DgType; +#define SBMP_DG_HSK_START 0x00 +#define SBMP_DG_HSK_ACCEPT 0x01 +#define SBMP_DG_HSK_CONFLICT 0x02 + +/** + * SBMP datagram object. + */ +typedef struct { + const uint8_t *payload; /*!< Datagram payload */ + SBMP_DgType type; /*!< Datagram type ID */ + uint16_t length; /*!< Datagram length (bytes) */ + uint16_t session; /*!< Datagram session number */ +} SBMP_Datagram; + + +/** + * @brief Convert a received buffer payload to a datagram. + * + * If the payload is < 3 bytes long, datagram can't be createdn and NULL + * is returned instead. The caller should then free the payload buffer. + * + * @param dg : datagram variable to populate, or NULL to allocate + * @param frame_payload : frame payload to parse + * @param length : frame payload length + * @return datagram (allocated if dg was NULL), or NULL if parsing failed. + */ +SBMP_Datagram *sbmp_dg_parse(SBMP_Datagram *dg_or_null, const uint8_t *frame_payload, uint16_t length); + +/** + * @brief Start a datagram (and the frame) + * + * @param state : SBMP state struct + * @param cksum_type : Checksum type to use for the frame; 0 - none, 32 - CRC32 + * @param session : session number + * @param type : Datagram type ID + * @param length : Datagram payload length (bytes) + * @return success + */ +bool sbmp_dg_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t session, SBMP_DgType type, uint16_t length); + +/** + * @brief Send a complete prepared datagram, also starts the frame. + * + * @param state : SBMP state struct + * @param cksum_type : Checksum type to use for the frame; 0 - none, 32 - CRC32 + * @param dg : Datagram struct containing DG settings and the payload. + * @return success + */ +bool sbmp_dg_send(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, SBMP_Datagram *dg); + + +#endif /* SBMP_DATAGRAM_H */ diff --git a/sbmp/sbmp_frame.c b/sbmp/sbmp_frame.c new file mode 100644 index 0000000..4e8ff22 --- /dev/null +++ b/sbmp/sbmp_frame.c @@ -0,0 +1,442 @@ +#include "esp8266.h" + +#include + +#include "sbmp_config.h" +#include "sbmp_logging.h" +#include "sbmp_checksum.h" +#include "sbmp_frame.h" + +// protos +static void call_frame_rx_callback(SBMP_FrmInst *frm); + + +/** Allocate the state struct & init all fields */ +SBMP_FrmInst *sbmp_frm_init( + SBMP_FrmInst *frm, + uint8_t *buffer, + uint16_t buffer_size, + void (*rx_handler)(uint8_t *, uint16_t, void *), + void (*tx_func)(uint8_t)) +{ + +#if SBMP_MALLOC + + if (frm == NULL) { + // caller wants us to allocate it + frm = malloc(sizeof(SBMP_FrmInst)); + } + + if (buffer == NULL) { + // caller wants us to allocate it + buffer = malloc(buffer_size); + } + +#else + + if (frm == NULL || buffer == NULL) { + return NULL; // malloc not enabled, fail + } + +#endif + + frm->rx_handler = rx_handler; + + frm->rx_buffer = buffer; + frm->rx_buffer_cap = buffer_size; + + frm->user_token = NULL; // NULL if not set + + frm->tx_func = tx_func; + + frm->rx_enabled = false; + frm->tx_enabled = false; + + sbmp_frm_reset(frm); + + return frm; +} + +/** Reset the internal state */ +void sbmp_frm_reset(SBMP_FrmInst *frm) +{ + sbmp_frm_reset_rx(frm); + sbmp_frm_reset_tx(frm); +} + +/** Enable or disable Rx */ +void sbmp_frm_enable_rx(SBMP_FrmInst *frm, bool enable) +{ + frm->rx_enabled = enable; +} + +/** Enable or disable Tx */ +void sbmp_frm_enable_tx(SBMP_FrmInst *frm, bool enable) +{ + frm->tx_enabled = enable; +} + +/** Enable or disable both Rx and Tx */ +void sbmp_frm_enable(SBMP_FrmInst *frm, bool enable) +{ + sbmp_frm_enable_rx(frm, enable); + sbmp_frm_enable_tx(frm, enable); +} + +/** Set user token */ +void sbmp_frm_set_user_token(SBMP_FrmInst *frm, void *token) +{ + frm->user_token = token; +} + +/** Reset the receiver state */ +void sbmp_frm_reset_rx(SBMP_FrmInst *frm) +{ + frm->rx_buffer_i = 0; + frm->rx_length = 0; + frm->mb_buf = 0; + frm->mb_cnt = 0; + frm->rx_hdr_xor = 0; + frm->rx_cksum_scratch = 0; + frm->rx_cksum_type = SBMP_CKSUM_NONE; + frm->rx_status = FRM_STATE_IDLE; +// printf("---- RX RESET STATE ----\n"); +} + +/** Reset the transmitter state */ +void sbmp_frm_reset_tx(SBMP_FrmInst *frm) +{ + frm->tx_status = FRM_STATE_IDLE; + frm->tx_remain = 0; + frm->tx_cksum_scratch = 0; + frm->tx_cksum_type = SBMP_CKSUM_NONE; +// printf("---- TX RESET STATE ----\n"); +} + +/** Update a header XOR */ +static inline +void hdrxor_update(SBMP_FrmInst *frm, uint8_t rxbyte) +{ + frm->rx_hdr_xor ^= rxbyte; +} + +/** Check header xor against received value */ +static inline +bool hdrxor_verify(SBMP_FrmInst *frm, uint8_t rx_xor) +{ + return frm->rx_hdr_xor == rx_xor; +} + +/** Append a byte to the rx buffer */ +static inline +void append_rx_byte(SBMP_FrmInst *frm, uint8_t b) +{ + frm->rx_buffer[frm->rx_buffer_i++] = b; +} + +/** Set n-th byte (0 = LSM) to a value */ +static inline +void set_byte(uint32_t *acc, uint8_t pos, uint8_t byte) +{ + *acc |= (uint32_t)(byte << (pos * 8)); +} + +/** + * Call the message handler with the payload. + * + */ +static void call_frame_rx_callback(SBMP_FrmInst *frm) +{ + if (frm->rx_handler == NULL) { + sbmp_error("frame_handler is null!"); + return; + } + + frm->rx_status = FRM_STATE_WAIT_HANDLER; + frm->rx_handler(frm->rx_buffer, frm->rx_length, frm->user_token); +} + +/** + * @brief Receive a byte + * + * SOF 8 | CKSUM_TYPE 8 | LEN 16 | PAYLOAD | CKSUM 0/4 + * + * @param state + * @param rxbyte + * @return status + */ +SBMP_RxStatus sbmp_frm_receive(SBMP_FrmInst *frm, uint8_t rxbyte) +{ + if (! frm->rx_enabled) { + return SBMP_RX_DISABLED; + } + + SBMP_RxStatus retval = SBMP_RX_OK; + + switch (frm->rx_status) { + case FRM_STATE_WAIT_HANDLER: + retval = SBMP_RX_BUSY; + break; + + case FRM_STATE_IDLE: + // can be first byte of a packet + + if (rxbyte == 0x01) { // start byte + + hdrxor_update(frm, rxbyte); + + frm->rx_status = FRM_STATE_CKSUM_TYPE; + } else { + // bad char + retval = SBMP_RX_INVALID; + } + break; + + case FRM_STATE_CKSUM_TYPE: + frm->rx_cksum_type = rxbyte; // checksum type received + + hdrxor_update(frm, rxbyte); + + // next will be length + frm->rx_status = FRM_STATE_LENGTH; + // clear MB for 2-byte length + frm->mb_buf = 0; + frm->mb_cnt = 0; + break; + + case FRM_STATE_LENGTH: + // append to the multi-byte buffer + set_byte(&frm->mb_buf, frm->mb_cnt++, rxbyte); + + hdrxor_update(frm, rxbyte); + + // if last of the MB field + if (frm->mb_cnt == 2) { + + // next will be the payload + uint16_t len = (uint16_t) frm->mb_buf; + frm->rx_length = len; + + if (len == 0) { + sbmp_error("Rx packet with no payload!"); + sbmp_frm_reset_rx(frm); // abort + break; + } + + frm->rx_status = FRM_STATE_HDRXOR; + } + break; + + case FRM_STATE_HDRXOR: + if (! hdrxor_verify(frm, rxbyte)) { + sbmp_error("Header XOR mismatch!"); + sbmp_frm_reset_rx(frm); // abort + break; + } + + // Check if not too long + if (frm->rx_length > frm->rx_buffer_cap) { + sbmp_error("Rx packet too long - %d!", (uint16_t)frm->rx_length); + // discard the rest + checksum + frm->rx_status = FRM_STATE_DISCARD; + frm->rx_length += chksum_length(frm->rx_cksum_type); + break; + } + + frm->rx_status = FRM_STATE_PAYLOAD; + cksum_begin(frm->rx_cksum_type, &frm->rx_cksum_scratch); + break; + + case FRM_STATE_DISCARD: + frm->rx_buffer_i++; + if (frm->rx_buffer_i == frm->rx_length) { + // done + sbmp_frm_reset_rx(frm); // go IDLE + } + break; + + case FRM_STATE_PAYLOAD: + append_rx_byte(frm, rxbyte); + cksum_update(frm->rx_cksum_type, &frm->rx_cksum_scratch, rxbyte); + + if (frm->rx_buffer_i == frm->rx_length) { + // payload rx complete + + if (frm->rx_cksum_type != SBMP_CKSUM_NONE) { + // receive the checksum + frm->rx_status = FRM_STATE_CKSUM; + + // clear MB for the 4-byte length + frm->mb_buf = 0; + frm->mb_cnt = 0; + } else { + // no checksum + // fire the callback + call_frame_rx_callback(frm); + + // clear + sbmp_frm_reset_rx(frm); + } + } + break; + + case FRM_STATE_CKSUM: + // append to the multi-byte buffer + set_byte(&frm->mb_buf, frm->mb_cnt++, rxbyte); + + // if last of the MB field + if (frm->mb_cnt == chksum_length(frm->rx_cksum_type)) { + + if (cksum_verify(frm->rx_cksum_type, &frm->rx_cksum_scratch, frm->mb_buf)) { + call_frame_rx_callback(frm); + } else { + sbmp_error("Rx checksum mismatch!"); + } + + // clear, enter IDLE + sbmp_frm_reset_rx(frm); + } + break; + } + + return retval; +} + +/** Send a frame header */ +bool sbmp_frm_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t length) +{ + if (! frm->tx_enabled) { + sbmp_error("Can't tx, not enabled."); + return false; + } + + if (frm->tx_status != FRM_STATE_IDLE) { + sbmp_error("Can't tx, busy."); + return false; + } + + if (frm->tx_func == NULL) { + sbmp_error("Can't tx, no tx func!"); + return false; + } + + if (cksum_type == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) { + sbmp_error("CRC32 disabled, using XOR for Tx."); + cksum_type = SBMP_CKSUM_XOR; + } + + sbmp_frm_reset_tx(frm); + + frm->tx_cksum_type = cksum_type; + frm->tx_remain = length; + frm->tx_status = FRM_STATE_PAYLOAD; + + // Send the header + + uint16_t len = (uint16_t) length; + + uint8_t hdr[4] = { + 0x01, + cksum_type, + len & 0xFF, + (len >> 8) & 0xFF + }; + + uint8_t hdr_xor = 0; + for (int i = 0; i < 4; i++) { + hdr_xor ^= hdr[i]; + frm->tx_func(hdr[i]); + } + + frm->tx_func(hdr_xor); + + cksum_begin(frm->tx_cksum_type, &frm->tx_cksum_scratch); + + return true; +} + +/** End frame and enter idle mode */ +static void end_frame(SBMP_FrmInst *frm) +{ + if (!frm->tx_enabled) { + sbmp_error("Can't tx, not enabled."); + return; + } + + cksum_end(frm->tx_cksum_type, &frm->tx_cksum_scratch); + + uint32_t cksum = frm->tx_cksum_scratch; + + switch (frm->tx_cksum_type) { + case SBMP_CKSUM_NONE: + break; // do nothing + + case SBMP_CKSUM_XOR: + // 1-byte checksum + frm->tx_func(cksum & 0xFF); + break; + + case SBMP_CKSUM_CRC32: + frm->tx_func(cksum & 0xFF); + frm->tx_func((cksum >> 8) & 0xFF); + frm->tx_func((cksum >> 16) & 0xFF); + frm->tx_func((cksum >> 24) & 0xFF); + } + + frm->tx_status = FRM_STATE_IDLE; // tx done +} + +/** Send a byte in the currently open frame */ +bool sbmp_frm_send_byte(SBMP_FrmInst *frm, uint8_t byte) +{ + if (!frm->tx_enabled) { + sbmp_error("Can't tx, not enabled."); + return false; + } + + if (frm->tx_remain == 0 || frm->tx_status != FRM_STATE_PAYLOAD) { + return false; + } + + frm->tx_func(byte); + cksum_update(frm->tx_cksum_type, &frm->tx_cksum_scratch, byte); + frm->tx_remain--; + + if (frm->tx_remain == 0) { + end_frame(frm); // checksum & go idle + } + + return true; +} + +/** Send a buffer in the currently open frame */ +uint16_t sbmp_frm_send_buffer(SBMP_FrmInst *frm, const uint8_t *buffer, uint16_t length) +{ + if (! frm->tx_enabled) { + sbmp_error("Can't tx, not enabled."); + return false; + } + + if (frm->tx_status != FRM_STATE_PAYLOAD) { + return false; // invalid call + } + + if (length == 0) { + end_frame(frm); // checksum & go idle + return 0; + } + + if (frm->tx_remain == 0) { + return false; // write past EOF (this shouldn't happen) + } + + uint16_t remain = length; + while (frm->tx_status == FRM_STATE_PAYLOAD && remain-- > 0) { + if (! sbmp_frm_send_byte(frm, *buffer++)) { + remain++; // "push back" + break; + } + } + + return (length - remain); +} diff --git a/sbmp/sbmp_frame.h b/sbmp/sbmp_frame.h new file mode 100644 index 0000000..ce7ef99 --- /dev/null +++ b/sbmp/sbmp_frame.h @@ -0,0 +1,205 @@ +#ifndef SBMP_FRAME_H +#define SBMP_FRAME_H + +/** + * SBMP Frame parser. + * + * This is the lowest level of the SBMP stack. + * + * You can use the frame parser on it's own, if you don't the higher layers. + * You will still get checksums and other benefits. + */ + +#include +#include +#include + +#include "sbmp_config.h" +#include "sbmp_checksum.h" + +/** + * Status returned from the byte rx function. + */ +typedef enum { + SBMP_RX_OK = 0, /*!< Byte received */ + SBMP_RX_BUSY, /*!< SBMP busy with the last packet, try again */ + SBMP_RX_INVALID, /*!< The byte was rejected - can be ASCII debug, or just garbage. */ + SBMP_RX_DISABLED, /*!< The byte was rejected, because the frame parser is not enabled yet. */ +} SBMP_RxStatus; + +/** SBMP internal state (context). Allows having multiple SBMP interfaces. */ +typedef struct SBMP_FrmInstance_struct SBMP_FrmInst; + + +/** + * @brief Initialize the SBMP internal state struct + * + * @param frm : Pointer to the state struct to populate. NULL to allocate. + * @param buffer : Buffer for Rx. NULL to allocate. + * @param buffer_size : size of the Rx buffer (or how many bytes to allocate) + * @param rx_handler : callback when frame is received + * @param tx_func : function for sending bytes (USART Tx) + * @return pointer to the state struct (passed or allocated) + */ +SBMP_FrmInst *sbmp_frm_init( + SBMP_FrmInst *frm_or_null, + uint8_t *buffer_or_null, + uint16_t buffer_size, + void (*rx_handler)(uint8_t *payload, uint16_t length, void *user_token), + void (*tx_func)(uint8_t byte) +); + +/** + * @brief Set the user token value. + * + * The token will be passed to the Frame Rx callback. + * Can be used to identify higher level layer instance. + * + * If the token is not set, NULL will be used in the callback. + * + * @param frm : Framing layer instance + * @param token : pointer to arbitrary object. + */ +void sbmp_frm_set_user_token(SBMP_FrmInst *frm, void *token); + +/** + * @brief Reset the SBMP frm state, discard partial messages (both rx and tx). + * @param frm : Framing layer instance + */ +void sbmp_frm_reset(SBMP_FrmInst *frm); + +/** Reset the receiver state only */ +void sbmp_frm_reset_rx(SBMP_FrmInst *frm); + +/** Reset the transmitter state only */ +void sbmp_frm_reset_tx(SBMP_FrmInst *frm); + +/** + * @brief Enable or disable both Rx and Tx + * @param frm : Framing layer instance + * @param enable : true - enable, false - disable + */ +void sbmp_frm_enable(SBMP_FrmInst *frm, bool enable); + +/** + * @brief Enable or disable Rx & the frame parser + * @param frm : Framing layer instance + * @param enable_rx : true - enable, false - disable + */ +void sbmp_frm_enable_rx(SBMP_FrmInst *frm, bool enable_rx); + +/** + * @brief Enable or disable the frame builder & Tx + * @param frm : Framing layer instance + * @param enable_tx : true - enable, false - disable + */ +void sbmp_frm_enable_tx(SBMP_FrmInst *frm, bool enable_tx); + +/** + * @brief Handle an incoming byte + * + * @param frm : Framing layer instance + * @param rxbyte : byte received + * @return status + */ +SBMP_RxStatus sbmp_frm_receive(SBMP_FrmInst *frm, uint8_t rxbyte); + +/** + * @brief Start a frame transmission + * + * @param frm : Framing layer instance + * @param cksum_type : checksum to use (0, 32) + * @param length : payload length + * @return true if frame was started. + */ +bool sbmp_frm_start(SBMP_FrmInst *frm, SBMP_CksumType cksum_type, uint16_t length); + +/** + * @brief Send one byte in the open frame. + * + * If the payload was completed, checksum is be added and + * the frame is closed. + * + * @param frm : Framing layer instance + * @param byte : byte to send + * @return true on success (value did fit in a frame) + */ +bool sbmp_frm_send_byte(SBMP_FrmInst *frm, uint8_t byte); + +/** + * @brief Send a data buffer (or a part). + * + * If the payload was completed, checksum is be added and + * the frame is closed. + * + * @param frm : Framing layer instance + * @param buffer : buffer of bytes to send + * @param length : buffer length (byte count) + * @return actual sent length (until payload is full) + */ +uint16_t sbmp_frm_send_buffer(SBMP_FrmInst *frm, const uint8_t *buffer, uint16_t length); + + + +// ---- Internal frame struct ------------------------ + +/** SBMP framing layer Rx / Tx state */ +enum SBMP_FrmStatus { + FRM_STATE_IDLE, /*!< Ready to start/accept a frame */ + FRM_STATE_CKSUM_TYPE, /*!< Rx, waiting for checksum type (1 byte) */ + FRM_STATE_LENGTH, /*!< Rx, waiting for payload length (2 bytes) */ + FRM_STATE_HDRXOR, /*!< Rx, waiting for header XOR (1 byte) */ + FRM_STATE_PAYLOAD, /*!< Rx or Tx, payload rx/tx in progress. */ + FRM_STATE_DISCARD, /*!< Discard rx_length worth of bytes, then end */ + FRM_STATE_CKSUM, /*!< Rx, waiting for checksum (4 bytes) */ + FRM_STATE_WAIT_HANDLER, /*!< Rx, waiting for rx callback to process the payload */ +}; + +/** + * Internal state of the SBMP node + * Placed in the header to allow static allocation. + */ +struct SBMP_FrmInstance_struct { + + // --- reception --- + + uint32_t mb_buf; /*!< Multi-byte value buffer */ + uint8_t mb_cnt; + + uint8_t rx_hdr_xor; /*!< Header xor scratch field */ + + enum SBMP_FrmStatus rx_status; + + bool rx_enabled; /*!< Enable Rx. Disabled -> reject incoming bytes. */ + bool tx_enabled; /*!< Enable Rx. Disabled -> reject incoming bytes. */ + + uint8_t *rx_buffer; /*!< Incoming packet buffer */ + uint16_t rx_buffer_i; /*!< Buffer cursor */ + uint16_t rx_buffer_cap; /*!< Buffer capacity */ + + uint16_t rx_length; /*!< Total payload length */ + + SBMP_CksumType rx_cksum_type; /*!< Current packet's checksum type */ + uint32_t rx_cksum_scratch; /*!< crc aggregation field for received data */ + + void (*rx_handler)(uint8_t *payload, uint16_t length, void *user_token); /*!< Message received handler */ + + void *user_token; /*!< Arbitrary pointer set by the user. Passed to callbacks. + Can be used to identify instance of a higher layer. */ + + // --- transmission --- + + uint16_t tx_remain; /*!< Number of remaining bytes to transmit */ + SBMP_CksumType tx_cksum_type; + uint32_t tx_cksum_scratch; /*!< crc aggregation field for transmit */ + + enum SBMP_FrmStatus tx_status; + + // output functions. Only tx_func is needed. + void (*tx_func)(uint8_t byte); /*!< Function to send one byte */ +}; + +// ------------------------------------ + + +#endif /* SBMP_FRAME_H */ diff --git a/sbmp/sbmp_logging.c b/sbmp/sbmp_logging.c new file mode 100644 index 0000000..1599252 --- /dev/null +++ b/sbmp/sbmp_logging.c @@ -0,0 +1,6 @@ +#include + +#include "esp8266.h" + +#include "sbmp_config.h" +#include "sbmp_logging.h" diff --git a/sbmp/sbmp_logging.h b/sbmp/sbmp_logging.h new file mode 100644 index 0000000..e6ad08b --- /dev/null +++ b/sbmp/sbmp_logging.h @@ -0,0 +1,15 @@ +#ifndef SBMP_COMMON_H +#define SBMP_COMMON_H + +#include "sbmp_config.h" +#include "esp8266.h" + +/** + * SBMP logging utils + */ + +// do-nothing definitions +#define sbmp_error(fmt, ...) os_printf("\x1b[31;1mSBMP][E] " #fmt "\x1b[0m\r\n", ##__VA_ARGS__) +#define sbmp_info(fmt, ...) os_printf("\x1b[32;1m[SBMP][i] " #fmt "\x1b[0m\r\n", ##__VA_ARGS__) + +#endif /* SBMP_COMMON_H */ diff --git a/sbmp/sbmp_session.c b/sbmp/sbmp_session.c new file mode 100644 index 0000000..75dc246 --- /dev/null +++ b/sbmp/sbmp_session.c @@ -0,0 +1,416 @@ +#include "esp8266.h" + +#include "sbmp_config.h" +#include "sbmp_logging.h" +#include "sbmp_session.h" + +// protos +static void handle_hsk_datagram(SBMP_Endpoint *ep, SBMP_Datagram *dg); + +// lsb, msb for uint16_t +#define U16_LSB(x) ((x) & 0xFF) +#define U16_MSB(x) ((x >> 8) & 0xFF) + +// length of the payload sent with a handshake packet. +#define HSK_PAYLOAD_LEN 3 +// Datagram header length - 2 B sesn, 1 B type +#define DATAGRA_HEADER_LEN 3 + + +/** Rx handler that is assigned to the framing layer */ +static void ep_rx_handler(uint8_t *buf, uint16_t len, void *token) +{ + // endpoint pointer is stored in the user token + SBMP_Endpoint *ep = (SBMP_Endpoint *)token; + + if (NULL != sbmp_dg_parse(&ep->static_dg, buf, len)) { + // payload parsed OK + + // check if handshake datagram, else call user callback. + handle_hsk_datagram(ep, &ep->static_dg); + } +} + +/** + * @brief Initialize the endpoint. + * + * @param ep : Endpoint var pointer, or NULL to allocate one. + * @param buffer : Rx buffer. NULL to allocate one. + * @param buffer_size : Rx buffer length + * @return the endpoint struct pointer (allocated if ep was NULL) + */ +SBMP_Endpoint *sbmp_ep_init( + SBMP_Endpoint *ep, + uint8_t *buffer, + uint16_t buffer_size, + void (*dg_rx_handler)(SBMP_Datagram *dg), + void (*tx_func)(uint8_t byte)) +{ + if (ep == NULL) { + // request to allocate it + #if SBMP_MALLOC + ep = malloc(sizeof(SBMP_Endpoint)); + #else + return NULL; // fail + #endif + } + + // set up the framing layer + sbmp_frm_init(&ep->frm, buffer, buffer_size, ep_rx_handler, tx_func); + + // set token, so callback knows what EP it's for. + sbmp_frm_set_user_token(&ep->frm, (void *) ep); + + ep->rx_handler = dg_rx_handler; + ep->buffer_size = buffer_size; // sent to the peer + +#if SBMP_HAS_CRC32 + ep->peer_pref_cksum = SBMP_CKSUM_CRC32; + ep->pref_cksum = SBMP_CKSUM_CRC32; +#else + ep->peer_pref_cksum = SBMP_CKSUM_XOR; + ep->pref_cksum = SBMP_CKSUM_XOR; +#endif + + // reset state information + sbmp_ep_reset(ep); + + return ep; +} + +/** + * @brief Reset an endpoint and it's Framing Layer + * + * This discards all state information. + * + * @param ep : Endpoint + */ +void sbmp_ep_reset(SBMP_Endpoint *ep) +{ + ep->next_session = 0; + ep->origin = 0; + + // init the handshake status + ep->hsk_session = 0; + ep->hsk_state = SBMP_HSK_NOT_STARTED; + + ep->peer_buffer_size = 0xFFFF; // max possible buffer + + sbmp_frm_reset(&ep->frm); +} + + +// --- Customizing settings --- + +/** Set session number (good to randomize before first message) */ +void sbmp_ep_seed_session(SBMP_Endpoint *ep, uint16_t sesn) +{ + ep->next_session = sesn & 0x7FFF; +} + +/** Set the origin bit (bypass handshake) */ +void sbmp_ep_set_origin(SBMP_Endpoint *endp, bool bit) +{ + endp->origin = bit; +} + +/** Set the preferred checksum. */ +void sbmp_ep_set_preferred_cksum(SBMP_Endpoint *endp, SBMP_CksumType cksum_type) +{ + if (cksum_type == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) { + sbmp_error("CRC32 not avail, using XOR instead."); + cksum_type = SBMP_CKSUM_XOR; + } + + endp->pref_cksum = cksum_type; +} + + +/** Enable or disable RX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable_rx(SBMP_Endpoint *ep, bool enable_rx) +{ + sbmp_frm_enable_rx(&ep->frm, enable_rx); +} + +/** Enable or disable TX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable_tx(SBMP_Endpoint *ep, bool enable_tx) +{ + sbmp_frm_enable_tx(&ep->frm, enable_tx); +} + +/** Enable or disable Rx & TX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable(SBMP_Endpoint *ep, bool enable) +{ + sbmp_frm_enable(&ep->frm, enable); +} + +// --- + +/** Get a new session number */ +static uint16_t next_session(SBMP_Endpoint *ep) +{ + uint16_t sesn = ep->next_session; + + if (++ep->next_session == 0x8000) { + // overflow into the origin bit + ep->next_session = 0; // start from zero + } + + return sesn | (uint16_t)(ep->origin << 15); // add the origin bit +} + + +// --- Header/body send funcs --- + +/** Start a message as a reply */ +bool sbmp_ep_start_response(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t sesn) +{ + uint16_t peer_accepts = ep->peer_buffer_size - DATAGRA_HEADER_LEN; + + if (length > peer_accepts) { + sbmp_error("Msg too long (%d B), peer accepts max %d B.", length, peer_accepts); + return false; + } + + return sbmp_dg_start(&ep->frm, ep->peer_pref_cksum, sesn, type, length); +} + +/** Start a message in a new session */ +bool sbmp_ep_start_session(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t *sesn_ptr) +{ + uint16_t sn = next_session(ep); + + bool suc = sbmp_ep_start_response(ep, type, length, sn); + if (suc) { + if (sesn_ptr != NULL) *sesn_ptr = sn; + } + + return suc; +} + +/** Send one byte in the current message */ +bool sbmp_ep_send_byte(SBMP_Endpoint *ep, uint8_t byte) +{ + return sbmp_frm_send_byte(&ep->frm, byte); +} + +/** Send a data buffer (or a part) in the current message */ +uint16_t sbmp_ep_send_buffer(SBMP_Endpoint *ep, const uint8_t *buffer, uint16_t length) +{ + return sbmp_frm_send_buffer(&ep->frm, buffer, length); +} + +/** Rx, pass to framing layer */ +SBMP_RxStatus sbmp_ep_receive(SBMP_Endpoint *ep, uint8_t byte) +{ + return sbmp_frm_receive(&ep->frm, byte); +} + + +// --- All-in-one send funcs --- + +/** Send a message in a session. */ +bool sbmp_ep_send_response( + SBMP_Endpoint *ep, + SBMP_DgType type, + const uint8_t *buffer, + uint16_t length, + uint16_t sesn, + uint16_t *sent_bytes_ptr) +{ + bool suc = sbmp_ep_start_response(ep, type, length, sesn); + + if (suc) { + uint16_t sent = sbmp_ep_send_buffer(ep, buffer, length); + + if (sent_bytes_ptr != NULL) *sent_bytes_ptr = sent; + } + + return suc; +} + +/** Send message in a new session */ +bool sbmp_ep_send_message( + SBMP_Endpoint *ep, + SBMP_DgType type, + const uint8_t *buffer, + uint16_t length, + uint16_t *sesn_ptr, + uint16_t *sent_bytes_ptr) +{ + // This juggling with session nr is because it wouldn't work + // without actual hardware delay otherwise. + + uint16_t sn = next_session(ep);; + + uint16_t old_sesn = 0; + if (sesn_ptr != NULL) { + old_sesn = *sesn_ptr; + *sesn_ptr = sn; + } + + bool suc = sbmp_ep_send_response(ep, type, buffer, length, sn, sent_bytes_ptr); + + if (!suc) { + if (sesn_ptr != NULL) { + *sesn_ptr = old_sesn; // restore + } + } + + return suc; +} + + +// --- Handshake --- +/** + * Prepare a buffer to send to peer during handshake + * + * The buffer is long HSK_PAYLOAD_LEN bytes + */ +static void populate_hsk_buf(SBMP_Endpoint *ep, uint8_t* buf) +{ + // [ pref_crc 1B | buf_size 2B ] + + buf[0] = ep->pref_cksum; + buf[1] = U16_LSB(ep->buffer_size); + buf[2] = U16_MSB(ep->buffer_size); +} + +/** Parse peer info from received handhsake dg payload */ +static void parse_peer_hsk_buf(SBMP_Endpoint *ep, const uint8_t* buf) +{ + ep->peer_pref_cksum = buf[0]; + ep->peer_buffer_size = (uint16_t)(buf[1] | (buf[2] << 8)); + + sbmp_info("HSK success, peer buf %d, pref cksum %d", + ep->peer_buffer_size, + ep->peer_pref_cksum); + + // check if checksum available + if (ep->peer_pref_cksum == SBMP_CKSUM_CRC32 && !SBMP_HAS_CRC32) { + sbmp_error("CRC32 not avail, using XOR as peer's pref cksum."); + ep->peer_pref_cksum = SBMP_CKSUM_XOR; + } +} + +/** + * @brief Start a handshake (origin bit arbitration) + * @param ep : Endpoint state + */ +bool sbmp_ep_start_handshake(SBMP_Endpoint *ep) +{ + if (ep->hsk_state == SBMP_HSK_AWAIT_REPLY) { + // busy now + sbmp_error("Can't start HSK, in progress already."); + return false; + } + + ep->hsk_state = 0; + + uint8_t buf[HSK_PAYLOAD_LEN]; + populate_hsk_buf(ep, buf); + + ep->hsk_state = SBMP_HSK_AWAIT_REPLY; + + bool suc = sbmp_ep_send_message(ep, SBMP_DG_HSK_START, buf, 3, &ep->hsk_session, NULL); + + if (!suc) { + ep->hsk_state = SBMP_HSK_NOT_STARTED; + } + + return suc; +} + +/** Get hsk state */ +SBMP_HandshakeStatus sbmp_ep_handshake_status(SBMP_Endpoint *ep) +{ + return ep->hsk_state; +} + +/** + * @brief Process handshake datagrams & update handshake state accordingly. + * + * Non-handshake datagrams are passed on to the user Rx callback. + * + * @param ep : endpoint + * @param dg : datagram + */ +static void handle_hsk_datagram(SBMP_Endpoint *ep, SBMP_Datagram *dg) +{ + bool hsk_start = (dg->type == SBMP_DG_HSK_START); + bool hsk_accept = (dg->type == SBMP_DG_HSK_ACCEPT); + bool hsk_conflict = (dg->type == SBMP_DG_HSK_CONFLICT); + + if (hsk_start || hsk_accept || hsk_conflict) { + // prepare payload to send in response + uint8_t our_info_pld[HSK_PAYLOAD_LEN]; + populate_hsk_buf(ep, our_info_pld); + + //printf("hsk_state = %d, hsk_ses %d, dg_ses %d\n",ep->hsk_state, ep->hsk_session, dg->session); + + if (hsk_start) { + // peer requests origin + sbmp_info("Rx HSK request"); + + if (ep->hsk_state == SBMP_HSK_AWAIT_REPLY) { + // conflict occured - we're already waiting for a reply. + sbmp_ep_send_response(ep, SBMP_DG_HSK_CONFLICT, our_info_pld, HSK_PAYLOAD_LEN, dg->session, NULL); + ep->hsk_state = SBMP_HSK_CONFLICT; + + sbmp_error("HSK conflict"); + } else { + // we're idle, accept the request. + bool peer_origin = (dg->session & 0x8000) >> 15; + sbmp_ep_set_origin(ep, !peer_origin); + + // read peer's info + if (dg->length >= HSK_PAYLOAD_LEN) { + parse_peer_hsk_buf(ep, dg->payload); + } + + ep->hsk_state = SBMP_HSK_SUCCESS; + + // Send Accept response + sbmp_ep_send_response(ep, SBMP_DG_HSK_ACCEPT, our_info_pld, HSK_PAYLOAD_LEN, dg->session, NULL); + } + } else if (hsk_accept) { + // peer accepted our request + sbmp_info("Rx HSK accept"); + + if (ep->hsk_state != SBMP_HSK_AWAIT_REPLY || ep->hsk_session != dg->session) { + // but we didn't send any request + sbmp_error("Rx unexpected HSK accept, ignoring."); + + } else { + // OK, we were waiting for this reply + + // read peer's info + if (dg->length >= HSK_PAYLOAD_LEN) { + parse_peer_hsk_buf(ep, dg->payload); + } + + ep->hsk_state = SBMP_HSK_SUCCESS; + } + } else if (hsk_conflict) { + // peer rejected our request due to conflict + sbmp_info("Rx HSK conflict"); + + if (ep->hsk_state != SBMP_HSK_AWAIT_REPLY || ep->hsk_session != dg->session) { + // but we didn't send any request + sbmp_error("Rx unexpected HSK conflict, ignoring."); + } else { + // Acknowledge the conflict + + // reset everything + sbmp_frm_reset(&ep->frm); + + ep->hsk_state = SBMP_HSK_CONFLICT; + } + } + + } else { + // Not a HSK message + ep->rx_handler(dg); + } +} + diff --git a/sbmp/sbmp_session.h b/sbmp/sbmp_session.h new file mode 100644 index 0000000..b967d6f --- /dev/null +++ b/sbmp/sbmp_session.h @@ -0,0 +1,207 @@ +#ifndef SBMP_SESSION_H +#define SBMP_SESSION_H + +/** + * SBMP session layer + * + * This layer provides an abstraction over all of SBMP. + * + * Start by creating "endpoint" with sbmp_ep_init(), then configure and enable it. + * + * Next you should trigger a handshake, which assigns your endpoint the origin bit, + * and obtains information about your peer (it's buffer size and preferred checksum). + * + * You can still interact with the framing layer directly, but it shouldn't be needed. + */ + +#include +#include +#include + +#include "sbmp_config.h" +#include "sbmp_datagram.h" +#include "sbmp_frame.h" + +typedef enum { + SBMP_HSK_NOT_STARTED = 0, /*!< Initial state, unconfigured */ + SBMP_HSK_SUCCESS = 1, /*!< Handshake done, origin assigned. */ + SBMP_HSK_AWAIT_REPLY = 2, /*!< Request sent, awaiting a reply */ + SBMP_HSK_CONFLICT = 3, /*!< Conflict occured during HSK */ +} SBMP_HandshakeStatus; + + +/** SBMP Endpoint (session) structure */ +typedef struct { + bool origin; /*!< Local origin bit */ + uint16_t next_session; /*!< Next session number */ + + void (*rx_handler)(SBMP_Datagram *dg); /*!< Datagram receive handler */ + + SBMP_FrmInst frm; /*!< Framing layer internal state */ + + // Handshake + SBMP_HandshakeStatus hsk_state; /*!< Handshake progress */ + uint16_t hsk_session; /*!< Session number of the handshake request message */ + uint16_t peer_buffer_size; /*!< Peer's buffer size (obtained during handshake) */ + SBMP_CksumType peer_pref_cksum; /*!< Peer's preferred checksum type */ + + // Our info for the peer + uint16_t buffer_size; /*!< Our buffer size */ + SBMP_CksumType pref_cksum; /*!< Our preferred checksum */ + + SBMP_Datagram static_dg; /*!< Static datagram, used when DG is pased to a callback. + This way the datagram remains valid until next Frm Rx, + not only until the callback ends. Disabling the EP in the Rx + callback lets you preserve the Dg for a longer period - + i.e. put it in a global var, where a loop retrieves it. */ +} SBMP_Endpoint; + + +/** + * @brief Initialize the endpoint. + * + * @note about the Rx handler: + * The datagram is valid until a new payload byte is received by the Frm. + * Disable the endpoint (-> thus also Frm) in the callback if you need + * to keep the Dg longer. Then re-enable it after the Dg is processed. + * + * @param ep : Endpoint struct pointer, or NULL to allocate one. + * @param buffer : Rx buffer. NULL to allocate one. + * @param buffer_size : Rx buffer length + * @param dg_rx_handler : Datagram received handler. + * @param tx_func : Function to send a byte to USART + * @return the endpoint struct pointer (allocated if ep was NULL) + */ +SBMP_Endpoint *sbmp_ep_init(SBMP_Endpoint *ep, + uint8_t *buffer, + uint16_t buffer_size, + void (*dg_rx_handler)(SBMP_Datagram *dg), + void (*tx_func)(uint8_t byte)); + +/** + * @brief Reset an endpoint and it's Framing Layer + * + * This discards all state information. + * + * @param ep : Endpoint + */ +void sbmp_ep_reset(SBMP_Endpoint *ep); + +/** Set session number (good to randomize before starting a handshake) */ +void sbmp_ep_seed_session(SBMP_Endpoint *ep, uint16_t sesn); + +/** + * Set the origin bit. + * This can be used instead of handshake if origins are pre-assigned. + */ +void sbmp_ep_set_origin(SBMP_Endpoint *endp, bool bit); + +/** Set the preferred checksum for this peer. */ +void sbmp_ep_set_preferred_cksum(SBMP_Endpoint *endp, SBMP_CksumType cksum_type); + +/** + * @brief Start a message as a reply, with CRC32 + * + * @param ep : Endpoint struct + * @param type : Datagram type ID + * @param length : Datagram payload length (bytes) + * @param sesn : Session number of message this is a reply to. + * @return success + */ +bool sbmp_ep_start_response(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t sesn); + +/** + * @brief Start a message in a new session, with CRC32 + * + * @param ep : Endpoint struct + * @param type : Datagram type ID + * @param length : Datagram payload length (bytes) + * @param sesn : Var to store session number, NULL = don't store. + * @return success + */ +bool sbmp_ep_start_session(SBMP_Endpoint *ep, SBMP_DgType type, uint16_t length, uint16_t *sesn_ptr); + +/** + * @brief Send one byte in the current message + * + * @param ep : Endpoint struct + * @param byte : byte to send + * @return true on success + */ +bool sbmp_ep_send_byte(SBMP_Endpoint *ep, uint8_t byte); + +/** + * @brief Send a data buffer (or a part) in the current message + * + * @param ep : Endpoint struct + * @param buffer : buffer of bytes to send + * @param length : buffer length (byte count) + * @return actual sent length (until payload is full) + */ +uint16_t sbmp_ep_send_buffer(SBMP_Endpoint *ep, const uint8_t *buffer, uint16_t length); + +/** + * @brief Send a message in a new session. + * + * @param ep : Endpoint struct + * @param type : Datagram type ID + * @param buffer : Buffer with data to send + * @param length : Datagram payload length (bytes) + * @param sesn_ptr : Session number + * @param sent_bytes_ptr : Var to store NR of sent bytes. NULL = don't store. + * @return success + */ +bool sbmp_ep_send_response( + SBMP_Endpoint *ep, + SBMP_DgType type, + const uint8_t *buffer, + uint16_t length, + uint16_t sesn_ptr, + uint16_t *sent_bytes_ptr); + +/** + * @brief Send a message in a new session. + * + * @param ep : Endpoint struct + * @param type : Datagram type ID + * @param buffer : Buffer with data to send + * @param length : Datagram payload length (bytes) + * @param sesn_ptr : Var to store session number. NULL = don't store. + * @param sent_bytes_ptr : Var to store NR of sent bytes. NULL = don't store. + * @return success + */ +bool sbmp_ep_send_message( + SBMP_Endpoint *ep, + SBMP_DgType type, + const uint8_t *buffer, + uint16_t length, + uint16_t *sesn, + uint16_t *sent_bytes); + +/** + * @brief Start a handshake (origin bit arbitration) + * @param ep : Endpoint struct + */ +bool sbmp_ep_start_handshake(SBMP_Endpoint *ep); + +/** + * @brief Receive a byte from USART (is passed to the framing layer) + * @param ep : Endpoint struct + * @param byte : Byte received from USART + * @return true if byte was consumed + */ +SBMP_RxStatus sbmp_ep_receive(SBMP_Endpoint *ep, uint8_t byte); + +/** Get current handshake state */ +SBMP_HandshakeStatus sbmp_ep_handshake_status(SBMP_Endpoint *ep); + +/** Enable or disable both Rx and TX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable(SBMP_Endpoint *ep, bool enable); + +/** Enable or disable RX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable_rx(SBMP_Endpoint *ep, bool enable_rx); + +/** Enable or disable TX in the FrmInst backing this Endpoint */ +void sbmp_ep_enable_tx(SBMP_Endpoint *ep, bool enable_tx); + +#endif /* SBMP_SESSION_H */ diff --git a/user/datalink.c b/user/datalink.c new file mode 100644 index 0000000..6220856 --- /dev/null +++ b/user/datalink.c @@ -0,0 +1,27 @@ +#include "esp8266.h" +#include "uart_driver.h" +#include "sbmp.h" + +#include "datalink.h" + +/** func used for sending bytes by SBMP */ +static void u0_putc(uint8_t c) +{ + UART_WriteCharCRLF(UART0, c, 0); +} + + +static void dg_handler(SBMP_Datagram *dg) +{ + sbmp_info("Datagram received."); +} + + + +static SBMP_Endpoint *ep; + +/** Datalink */ +void datalinkInit(void) +{ + ep = sbmp_ep_init(NULL, NULL, 256, dg_handler, u0_putc); +} diff --git a/user/datalink.h b/user/datalink.h new file mode 100644 index 0000000..8d1b412 --- /dev/null +++ b/user/datalink.h @@ -0,0 +1,6 @@ +#ifndef DATALINK_H +#define DATALINK_H + +void datalinkInit(void); + +#endif // DATALINK_H diff --git a/user/cfg_serial.c b/user/serial.c similarity index 91% rename from user/cfg_serial.c rename to user/serial.c index db1cb81..9cf2556 100644 --- a/user/cfg_serial.c +++ b/user/serial.c @@ -14,12 +14,12 @@ -LOCAL void uart0_rx_intr_handler(void *para); -LOCAL void uart_recvTask(os_event_t *events); +static void uart0_rx_intr_handler(void *para); +static void uart_recvTask(os_event_t *events); #define uart_recvTaskPrio 0 #define uart_recvTaskQueueLen 10 -LOCAL os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; +static os_event_t uart_recvTaskQueue[uart_recvTaskQueueLen]; @@ -37,7 +37,7 @@ clear_rxtx(int uart_no) * @brief Configure UART 115200-8-N-1 * @param uart_no */ -void ICACHE_FLASH_ATTR +static void ICACHE_FLASH_ATTR my_uart_init(UARTn uart_no) { UART_SetParity(uart_no, PARITY_NONE); @@ -48,8 +48,8 @@ my_uart_init(UARTn uart_no) } -void ICACHE_FLASH_ATTR -serialInit() +/** Configure basic UART func and pins */ +static void conf_uart_pins(void) { // U0TXD PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); @@ -67,47 +67,46 @@ serialInit() // Select debug port UART_SetPrintPort(UART1); +} - - // Configure Rx on UART0 - - +/** Configure Rx on UART0 */ +static void conf_uart_receiver(void) +{ + // // Start the Rx reading task system_os_task(uart_recvTask, uart_recvTaskPrio, uart_recvTaskQueue, uart_recvTaskQueueLen); - - // set handler ETS_UART_INTR_ATTACH((void *)uart0_rx_intr_handler, &(UartDev.rcv_buff)); // the buf will be used as an arg - // fifo threshold config uint32_t conf = ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); conf |= ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S); - // timeout config conf |= ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S); // timeout threshold conf |= UART_RX_TOUT_EN; // enable timeout - WRITE_PERI_REG(UART_CONF1(UART0), conf); - // enable TOUT and ERR irqs SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA); - /* clear interrupt flags */ WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff); - /* enable RX interrupts */ SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_OVF_INT_ENA); - // Enable IRQ in Extensa ETS_UART_INTR_ENABLE(); } -// ---- receive business ---- +void ICACHE_FLASH_ATTR +serialInit() +{ + conf_uart_pins(); + conf_uart_receiver(); +} + +// ---- async receive stuff ---- void uart_rx_intr_disable(uint8 uart_no) @@ -129,7 +128,7 @@ void uart_rx_intr_enable(uint8 uart_no) #define UART_GetRxFifoCount(uart_no) ((READ_PERI_REG(UART_STATUS((uart_no))) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) -LOCAL void ICACHE_FLASH_ATTR +static void ICACHE_FLASH_ATTR uart_recvTask(os_event_t *events) { if (events->sig == 0) { @@ -158,7 +157,7 @@ uart_recvTask(os_event_t *events) * Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg * Returns : NONE *******************************************************************************/ -LOCAL void +static void uart0_rx_intr_handler(void *para) { uint32_t status_reg = READ_PERI_REG(UART_INT_ST(UART0)); diff --git a/user/cfg_serial.h b/user/serial.h similarity index 100% rename from user/cfg_serial.h rename to user/serial.h diff --git a/user/user_main.c b/user/user_main.c index 230345c..298ae53 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -21,8 +21,9 @@ // user files #include "cgi.h" -#include "cfg_serial.h" +#include "serial.h" #include "io.h" +#include "datalink.h" #include "uart_driver.h" @@ -114,12 +115,12 @@ static HttpdBuiltInUrl builtInUrls[] = { }; -static ETSTimer prTestTimer; +//static ETSTimer prTestTimer; -static void ICACHE_FLASH_ATTR test_timer_task(void *arg) { - const char * t = "Test\r\n"; - UART_WriteBuffer(0, (uint8_t*)t, strlen(t), 1000); -} +//static void ICACHE_FLASH_ATTR test_timer_task(void *arg) { +// const char * t = "Test\r\n"; +// UART_WriteBuffer(0, (uint8_t*)t, strlen(t), 1000); +//} /** @@ -133,11 +134,12 @@ void user_init(void) // reset button etc ioInit(); + // set up SBMP + datalinkInit(); + // Start the captive portal captdnsInit(); - /* --- Initialize ESPFS --- */ - // 0x40200000 is the base address for spi flash memory mapping, ESPFS_POS is the position // where image is written in flash that is defined in Makefile. #ifdef ESPFS_POS @@ -152,10 +154,10 @@ void user_init(void) os_printf("\nReady\n"); - // print TEST on the command interface every 500 ms - os_timer_disarm(&prTestTimer); - os_timer_setfn(&prTestTimer, test_timer_task, NULL); - os_timer_arm(&prTestTimer, 500, 1); +// // print TEST on the command interface every 500 ms +// os_timer_disarm(&prTestTimer); +// os_timer_setfn(&prTestTimer, test_timer_task, NULL); +// os_timer_arm(&prTestTimer, 500, 1); }