From c5e7dddfd1df54450ec56b86bcb94830f977be0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Tue, 2 Sep 2025 21:17:55 +0200 Subject: [PATCH] init --- .devcontainer/Dockerfile | 13 + .devcontainer/devcontainer.json | 21 + .gitignore | 4 + .vscode/c_cpp_properties.json | 23 + .vscode/launch.json | 15 + .vscode/settings.json | 14 + CMakeLists.txt | 8 + README.md | 32 ++ main/CMakeLists.txt | 2 + main/buscfg.h | 19 + main/main.c | 511 +++++++++++++++++ main/packetstructs.h | 107 ++++ main/sx1262.c | 970 ++++++++++++++++++++++++++++++++ main/sx1262.h | 435 ++++++++++++++ 14 files changed, 2174 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 main/CMakeLists.txt create mode 100644 main/buscfg.h create mode 100644 main/main.c create mode 100644 main/packetstructs.h create mode 100644 main/sx1262.c create mode 100644 main/sx1262.h diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..dafb8ad --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +ARG DOCKER_TAG=latest +FROM espressif/idf:${DOCKER_TAG} + +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 + +RUN apt-get update -y && apt-get install udev -y + +RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc + +ENTRYPOINT [ "/opt/esp/entrypoint.sh" ] + +CMD ["/bin/bash", "-c"] \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b801786 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "ESP-IDF QEMU", + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.defaultProfile.linux": "bash", + "idf.espIdfPath": "/opt/esp/idf", + "idf.toolsPath": "/opt/esp", + "idf.gitPath": "/usr/bin/git" + }, + "extensions": [ + "espressif.esp-idf-extension", + "espressif.esp-idf-web" + ] + } + }, + "runArgs": ["--privileged"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ef1448 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +sdkconfig +sdkconfig.old +.cache/ \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..7a61e51 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "ESP-IDF", + "compilerPath": "${config:idf.toolsPath}/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc", + "compileCommands": "${config:idf.buildPath}/compile_commands.json", + "includePath": [ + "${config:idf.espIdfPath}/components/**", + "${config:idf.espIdfPathWin}/components/**", + "${workspaceFolder}/**" + ], + "browse": { + "path": [ + "${config:idf.espIdfPath}/components", + "${config:idf.espIdfPathWin}/components", + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true + } + } + ], + "version": 4 +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2511a38 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "gdbtarget", + "request": "attach", + "name": "Eclipse CDT GDB Adapter" + }, + { + "type": "espidf", + "name": "Launch", + "request": "launch" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..12cc643 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "C_Cpp.intelliSenseEngine": "default", + "idf.espIdfPath": "/home/bruno/esp/master/esp-idf", + "idf.pythonInstallPath": "/usr/bin/python3", + "idf.openOcdConfigs": [ + "board/esp32s3-builtin.cfg" + ], + "idf.port": "/dev/ttyUSB0", + "idf.toolsPath": "/home/bruno/.espressif", + "idf.customExtraVars": { + "IDF_TARGET": "esp32s3" + }, + "idf.flashType": "UART" +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ba1c6d9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(coreimpl) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..455eb90 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# _Sample project_ + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This is the simplest buildable example. The example is used by command `idf.py create-project` +that copies the project to user specified path and set it's name. For more information follow the [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project) + + + +## How to use example +We encourage the users to use the example as a template for the new projects. +A recommended way is to follow the instructions on a [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project). + +## Example folder contents + +The project **sample_project** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main). + +ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` +files that provide set of directives and instructions describing the project's source files and targets +(executable, library, or both). + +Below is short explanation of remaining files in the project folder. + +``` +├── CMakeLists.txt +├── main +│   ├── CMakeLists.txt +│   └── main.c +└── README.md This is the file you are currently reading +``` +Additionally, the sample project contains Makefile and component.mk files, used for the legacy Make based build system. +They are not used or needed when building with CMake and idf.py. diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..71e54d0 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "sx1262.c" "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/main/buscfg.h b/main/buscfg.h new file mode 100644 index 0000000..fdf57c7 --- /dev/null +++ b/main/buscfg.h @@ -0,0 +1,19 @@ +#ifndef BUSCFG_FILE +#define BUSCFG_FILE + +#include "soc/gpio_num.h" + +#define ESP_USB_DP GPIO_NUM_20 +#define ESP_USB_DM GPIO_NUM_19 +#define ESP_RXD0 GPIO_NUM_44 +#define ESP_TXD0 GPIO_NUM_43 + +#define HSPI_MISO_GPIO GPIO_NUM_11 +#define HSPI_MOSI_GPIO GPIO_NUM_10 + +#define HSPI_SCK_GPIO GPIO_NUM_9 +#define HSPI_LORA_CS GPIO_NUM_8 +#define LORA_DIO1 GPIO_NUM_14 +#define LORA_BUSY GPIO_NUM_13 +#define LORA_RESET GPIO_NUM_12 +#endif \ No newline at end of file diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..91bbe6e --- /dev/null +++ b/main/main.c @@ -0,0 +1,511 @@ +#include "buscfg.h" +#include "driver/uart.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "mbedtls/base64.h" +#include "packetstructs.h" +#include "string.h" +#include "sx1262.h" +#include +#include +#include +#include +#include "mbedtls/aes.h" +#include "mbedtls/sha256.h" +#include "mbedtls/md.h" + +#define TAG "canZem" + +// requires at least a 256 byte data +FrameStruct decodeFrame(unsigned char *data, unsigned char dataLen) { + FrameStruct frame; + memset(&frame, 0, sizeof(frame)); + unsigned char index = 0; + frame.header = data[index++]; + if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT || + (frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) { + memcpy(frame.transportCodes, data + index, 4); + index += 4; + } + frame.pathLen = data[index++]; + + memcpy(frame.path, data + index, frame.pathLen); + index += frame.pathLen; + frame.payloadLen = dataLen - index; + memcpy(frame.payload, data + index, frame.payloadLen); + + return frame; +} + + +#define KEY_SIZE 16 // 128-bit AES +#define HMAC_SIZE 2 // SHA256 output size + +int aes_encrypt_ecb(const uint8_t *key, + const uint8_t *input, size_t ilen, + uint8_t *output) +{ + if (ilen % 16 != 0) { + return -1; // must be multiple of 16 + } + + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_enc(&ctx, key, 128); + + for (size_t offset = 0; offset < ilen; offset += 16) { + mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, + input + offset, output + offset); + } + + mbedtls_aes_free(&ctx); + return 0; +} + +// AES-ECB decrypt (same as Arduino's aes.decryptBlock) +int aes_decrypt_ecb(const uint8_t *key, + const uint8_t *input, size_t ilen, + uint8_t *output) +{ + if (ilen % 16 != 0) { + return -1; // must be multiple of 16 + } + + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_dec(&ctx, key, 128); + + for (size_t offset = 0; offset < ilen; offset += 16) { + mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, + input + offset, output + offset); + } + + mbedtls_aes_free(&ctx); + return 0; +} + +// HMAC-SHA256 +int hmac_sha256(const uint8_t *key, size_t keylen, + const uint8_t *input, size_t ilen, + uint8_t *output) +{ + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + return mbedtls_md_hmac(md_info, key, keylen, input, ilen, output); +} + + + +// Verify MAC + Decrypt +int mac_then_decrypt(const uint8_t *aes_key, + const uint8_t *input, size_t ilen, + uint8_t *plaintext, size_t *plen) +{ + if (ilen <= HMAC_SIZE) { + fprintf(stderr, "[mac_then_decrypt] input too short (ilen=%zu)\n", ilen); + return -1; + } + + const uint8_t *mac = input; + const uint8_t *ciphertext = input + HMAC_SIZE; + size_t clen = ilen - HMAC_SIZE; + + uint8_t calc_mac[32]; // full SHA256 + int ret = hmac_sha256(aes_key, KEY_SIZE, ciphertext, clen, calc_mac); + if (ret != 0) { + fprintf(stderr, "[mac_then_decrypt] hmac_sha256() failed: %d\n", ret); + return -4; + } + + // Debug dump of MACs + //fprintf(stderr, "[mac_then_decrypt] expected MAC (from input): "); + //for (size_t i = 0; i < HMAC_SIZE; i++) fprintf(stderr, "%02X", mac[i]); + //fprintf(stderr, "\n"); + + //fprintf(stderr, "[mac_then_decrypt] calculated MAC: "); + //for (size_t i = 0; i < HMAC_SIZE; i++) fprintf(stderr, "%02X", calc_mac[i]); + //fprintf(stderr, "\n"); + + // compare only first HMAC_SIZE bytes + if (memcmp(mac, calc_mac, HMAC_SIZE) != 0) { + fprintf(stderr, "[mac_then_decrypt] MAC check failed\n"); + return -2; + } + + // AES-ECB decrypt (parity with Arduino) + ret = aes_decrypt_ecb(aes_key, ciphertext, clen, plaintext); + if (ret != 0) { + fprintf(stderr, "[mac_then_decrypt] aes_decrypt_ecb() failed: %d\n", ret); + return -3; + } + + *plen = clen; + //fprintf(stderr, "[mac_then_decrypt] success, decrypted %zu bytes\n", *plen); + return 0; +} + + +void hexdump(const char *label, const uint8_t *data, size_t len) { + if (label) + printf("%s (len=%zu):\n", label, len); + + for (size_t i = 0; i < len; i += 16) { + printf("%04zx ", i); // offset + for (size_t j = 0; j < 16; j++) { + if (i + j < len) + printf("%02X ", data[i + j]); + else + printf(" "); // pad spacing + } + printf(" "); + for (size_t j = 0; j < 16 && i + j < len; j++) { + uint8_t c = data[i + j]; + printf("%c", isprint(c) ? c : '.'); + } + printf("\n"); + } +} + +GroupTextMessage decodeGroupMessage(FrameStruct frame) { + GroupTextMessage msg; + memset(&msg, 0, sizeof(msg)); + if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_GRP_TXT) { + return msg; + } + unsigned char index = 0; + msg.channelHash = frame.payload[index++]; + unsigned char tmp[200]; + + const uint8_t pubAes[16] = {0x8b, 0x33, 0x87, 0xe9, 0xc5, 0xcd, 0xea, 0x6a, 0xc9, 0xe5, + 0xed, 0xba, 0xa1, 0x15, 0xcd, 0x72}; + + uint8_t aes_key[16]; + + if (msg.channelHash == 17) { + memcpy(aes_key, pubAes, sizeof(aes_key)); + } else { + return msg; + } + + size_t plaintextLen = 0; + + mac_then_decrypt(aes_key, frame.payload + index, frame.payloadLen - index, tmp, &plaintextLen); + if (plaintextLen == 0) { + printf("error decrypting"); + } + + index = 0; + + memcpy(&msg.timestamp, tmp + index, 4); + index += 4; + msg.flags = tmp[index++]; + + memcpy(msg.text, tmp + index, plaintextLen - index); + return msg; +} + +void printGroupMessage(GroupTextMessage msg) { + printf("Message with channel hash %d, flags %d: %s\n", msg.channelHash, msg.flags, msg.text); +} + +AdvertisementPayload decodeAdvertisement(FrameStruct frame) { + AdvertisementPayload advert; + memset(&advert, 0, sizeof(advert)); + + if ((frame.header & PAYLOAD_TYPE_MASK) != PAYLOAD_TYPE_ADVERT) { + return advert; + } + + unsigned char index = 0; + + memcpy(advert.pubKey, frame.payload + index, 32); + index += 32; + + memcpy(&advert.timestamp, frame.payload + index, 4); + index += 4; + + memcpy(advert.signature, frame.payload + index, 64); + index += 64; + + advert.dataFlags = frame.payload[index++]; + + if (advert.dataFlags & ADVERTISEMENT_FLAG_HAS_LOCATION) { + memcpy(&advert.latitude, frame.payload + index, 4); + index += 4; + memcpy(&advert.longitude, frame.payload + index, 4); + index += 4; + } + + if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU1) { + memcpy(&advert.rfu1, frame.payload + index, 2); + index += 2; + } + if (advert.dataFlags & ADVERTISEMENT_FLAG_RFU2) { + memcpy(&advert.rfu2, frame.payload + index, 2); + index += 2; + } + memcpy(advert.nodeName, frame.payload + index, frame.payloadLen - index); + advert.nodeName[frame.payloadLen - index] = 0; + + return advert; +} + +void printAdvertisement(AdvertisementPayload advert) { + size_t keyB64len = 0; + size_t sigB64len = 0; + unsigned char keyBuf[50]; + unsigned char sigBuf[90]; + memset(keyBuf, 0, sizeof(keyBuf)); + memset(sigBuf, 0, sizeof(sigBuf)); + mbedtls_base64_encode(keyBuf, sizeof(keyBuf), &keyB64len, advert.pubKey, 32); + mbedtls_base64_encode(sigBuf, sizeof(sigBuf), &sigB64len, advert.signature, + 64); + + printf("%s on %ld with type %s on %s location %ld %ld, public key %s and " + "signature %s\n", + advert.dataFlags & ADVERTISEMENT_FLAG_HAS_NAME ? advert.nodeName + : "nameless node", + advert.timestamp, + (advert.dataFlags & 0x07) == 0x04 + ? "sensor" + : ((advert.dataFlags & 0x07) == 0x03 + ? "room server" + : ((advert.dataFlags & 0x07) == 0x02 ? "repeater" + : "chat node")), + advert.dataFlags & 0x80 ? "known" : "unknown", advert.latitude, + advert.longitude, keyBuf, sigBuf); +} + +void printFrameHeader(FrameStruct frame) { + char strBuf[512] = "Frame route is "; + switch (frame.header & ROUTE_TYPE_MASK) { + case ROUTE_TYPE_TRANSPORT_FLOOD: + strcat(strBuf, "transport flood"); + break; + + case ROUTE_TYPE_FLOOD: + strcat(strBuf, "flood"); + break; + + case ROUTE_TYPE_DIRECT: + strcat(strBuf, "direct"); + break; + + case ROUTE_TYPE_TRANSPORT_DIRECT: + strcat(strBuf, "transport direct"); + break; + } + + strcat(strBuf, ", payload type is "); + + switch (frame.header & PAYLOAD_TYPE_MASK) { + case PAYLOAD_TYPE_REQ: + strcat(strBuf, "request"); + break; + + case PAYLOAD_TYPE_RESPONSE: + strcat(strBuf, "response"); + break; + + case PAYLOAD_TYPE_TXT_MSG: + strcat(strBuf, "text message"); + break; + + case PAYLOAD_TYPE_ACK: + strcat(strBuf, "acknowledgement"); + break; + + case PAYLOAD_TYPE_ADVERT: + strcat(strBuf, "advert"); + break; + + case PAYLOAD_TYPE_GRP_TXT: + strcat(strBuf, "group text"); + break; + + case PAYLOAD_TYPE_GRP_DATA: + strcat(strBuf, "group data"); + break; + + case PAYLOAD_TYPE_ANON_REQ: + strcat(strBuf, "anon request"); + break; + + case PAYLOAD_TYPE_PATH: + strcat(strBuf, "path"); + break; + + case PAYLOAD_TYPE_TRACE: + strcat(strBuf, "trace"); + break; + + case PAYLOAD_TYPE_MULTIPART: + strcat(strBuf, "multipart"); + break; + + case PAYLOAD_TYPE_RAW_CUSTOM: + strcat(strBuf, "raw"); + break; + } + char version[2]; + version[0] = (frame.header >> 6) + '0'; + version[1] = 0; + + strcat(strBuf, ", payload version is "); + + strcat(strBuf, version); + + puts(strBuf); + + if ((frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_DIRECT || + (frame.header & ROUTE_TYPE_MASK) == ROUTE_TYPE_TRANSPORT_FLOOD) { + printf("Transport codes: %d %d\n", *((uint16_t *)frame.transportCodes), + *((uint16_t *)&(frame.transportCodes[2]))); + } + printf("Path is %d nodes long", frame.pathLen); + + for (uint8_t pathIndex = 0; pathIndex < frame.pathLen; pathIndex++) { + printf("node %d - %02X, ", pathIndex, frame.path[pathIndex]); + } + putchar('\n'); + + char payloadBuf[185]; + memset(payloadBuf, 0, sizeof(payloadBuf)); + memcpy(payloadBuf, frame.payload, frame.payloadLen); + + // unsigned char outBuf[345]; + // memset(outBuf, 0, sizeof(outBuf)); + // size_t output_len = 0; // will store the real output size + // mbedtls_base64_encode(outBuf, sizeof(outBuf), &output_len, frame.payload, + // frame.payloadLen); printf("Payload L%d: %s\n", output_len, outBuf); + + // printf("\npayload is %s \n", payloadBuf); +} + +void app_main(void) { + + uart_set_baudrate(UART_NUM_0, 115200); + ESP_LOGI("BOOT", "BRN Systems incorporated MeshCore firmware build: %s %s", + __DATE__, __TIME__); + + spi_bus_config_t HighSpeedBusCfg = { + // CONNECTED to LoRa and SD card + .mosi_io_num = HSPI_MOSI_GPIO, .miso_io_num = HSPI_MISO_GPIO, + .sclk_io_num = HSPI_SCK_GPIO, .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 256, // probably change + }; + ESP_ERROR_CHECK( + spi_bus_initialize(SPI3_HOST, &HighSpeedBusCfg, SPI_DMA_CH_AUTO)); + + const int64_t interval_us = 10000; // 10 ms + int64_t start_time, end_time, elapsed; + + LoRaInit(); + int8_t txPowerInDbm = 20; + uint32_t frequencyInHz = 869525000; + + ESP_LOGW(TAG, "Enable TCXO"); + // float tcxoVoltage = 2.2; //ebyte + float tcxoVoltage = 1.8; // heltec + bool useRegulatorLDO = true; + + LoRaDebugPrint(false); + if (LoRaBegin(frequencyInHz, txPowerInDbm, tcxoVoltage, useRegulatorLDO) != + 0) { + ESP_LOGE(TAG, "Does not recognize the module"); + while (1) { + vTaskDelay(1); + } + } + + uint8_t spreadingFactor = 11; + uint8_t bandwidth = SX126X_LORA_BW_250_0; + uint8_t codingRate = SX126X_LORA_CR_4_5; + uint16_t preambleLength = 16; + bool crcOn = true; + bool invertIrq = false; + + LoRaConfig(spreadingFactor, bandwidth, codingRate, preambleLength, 0, crcOn, + invertIrq); + + uint8_t bufIn[256]; + + while (1) { + start_time = esp_timer_get_time(); + + uint8_t rxLen = LoRaReceive(bufIn, sizeof(bufIn)); + if (rxLen > 0) { + // ESP_LOGI(TAG, "%d byte packet received", rxLen); + + int8_t rssi, snr; + GetPacketStatus(&rssi, &snr); + // ESP_LOGI(TAG, "rssi=%d[dBm] snr=%d[dB]", rssi, snr); + + FrameStruct frame = decodeFrame(bufIn, rxLen); + + printFrameHeader(frame); + + switch (frame.header & PAYLOAD_TYPE_MASK) { + case PAYLOAD_TYPE_REQ: + break; + + case PAYLOAD_TYPE_RESPONSE: + break; + + case PAYLOAD_TYPE_TXT_MSG: + break; + + case PAYLOAD_TYPE_ACK: + unsigned char checkSumBuf[10]; + memset(checkSumBuf, 0, sizeof(checkSumBuf)); + size_t checksumLen = 0; + mbedtls_base64_encode(checkSumBuf, sizeof(checkSumBuf), &checksumLen, + frame.payload, 4); + printf("Checksum: %s\n", checkSumBuf); + break; + + case PAYLOAD_TYPE_ADVERT: + AdvertisementPayload advert = decodeAdvertisement(frame); + printAdvertisement(advert); + break; + + case PAYLOAD_TYPE_GRP_TXT: + GroupTextMessage msg = decodeGroupMessage(frame); + printGroupMessage(msg); + break; + + case PAYLOAD_TYPE_GRP_DATA: + break; + + case PAYLOAD_TYPE_ANON_REQ: + break; + + case PAYLOAD_TYPE_PATH: + break; + + case PAYLOAD_TYPE_TRACE: + break; + + case PAYLOAD_TYPE_MULTIPART: + break; + + case PAYLOAD_TYPE_RAW_CUSTOM: + break; + } + } + + int lost = GetPacketLost(); + if (lost != 0) { + ESP_LOGW(TAG, "%d packets lost", lost); + } + + end_time = esp_timer_get_time(); + elapsed = end_time - start_time; + + if (elapsed < interval_us) { + vTaskDelay(pdMS_TO_TICKS((interval_us - elapsed) / 1000)); + } + } +} \ No newline at end of file diff --git a/main/packetstructs.h b/main/packetstructs.h new file mode 100644 index 0000000..4903489 --- /dev/null +++ b/main/packetstructs.h @@ -0,0 +1,107 @@ +#ifndef PACKETSTRUCTS_FILE +#define PACKETSTRUCTS_FILE + +#include +#define ROUTE_TYPE_MASK 0x03 +#define PAYLOAD_TYPE_MASK 0x3C +#define PAYLOAD_VERSION_MASK 0xC0 + +typedef enum RouteType { + ROUTE_TYPE_TRANSPORT_FLOOD = 0x00, + ROUTE_TYPE_FLOOD = 0x01, + ROUTE_TYPE_DIRECT = 0x02, + ROUTE_TYPE_TRANSPORT_DIRECT = 0x03, +} RouteType; + + +typedef enum PayloadType { + PAYLOAD_TYPE_REQ = 0x00 << 2, + PAYLOAD_TYPE_RESPONSE = 0x01 << 2, + PAYLOAD_TYPE_TXT_MSG = 0x02 << 2, + PAYLOAD_TYPE_ACK = 0x03 << 2, + PAYLOAD_TYPE_ADVERT = 0x04 << 2, + PAYLOAD_TYPE_GRP_TXT = 0x05 << 2, + PAYLOAD_TYPE_GRP_DATA = 0x06 << 2, + PAYLOAD_TYPE_ANON_REQ = 0x07 << 2, + PAYLOAD_TYPE_PATH = 0x08 << 2, + PAYLOAD_TYPE_TRACE = 0x09 << 2, + PAYLOAD_TYPE_MULTIPART = 0x0A << 2, + PAYLOAD_TYPE_RAW_CUSTOM = 0x0F << 2, +} PayloadType; + + +typedef enum PayloadVersion { + PAYLOAD_VERSION_0 = 0 << 6, + PAYLOAD_VERSION_1 = 1 << 6, + PAYLOAD_VERSION_2 = 2 << 6, + PAYLOAD_VERSION_3 = 3 << 6, +} PayloadVersion; + + +typedef struct FrameStruct { + unsigned char header; + unsigned char transportCodes[4]; + unsigned char pathLen; + unsigned char path[64]; + unsigned char payloadLen; + unsigned char payload[184]; +} FrameStruct; + +typedef enum AdvertisementPayloadFlags { + ADVERTISEMENT_FLAG_IS_CHAT_NODE = 0x01, + ADVERTISEMENT_FLAG_IS_REAPEATER = 0x02, + ADVERTISEMENT_FLAG_IS_ROOM_SERVER = 0x03, + ADVERTISEMENT_FLAG_IS_SENSOR = 0x04, + ADVERTISEMENT_FLAG_HAS_LOCATION = 0x10, + ADVERTISEMENT_FLAG_RFU1 = 0x20, + ADVERTISEMENT_FLAG_RFU2 = 0x40, + ADVERTISEMENT_FLAG_HAS_NAME = 0x80, +} AdvertisementPayloadFlags; + +typedef struct AdvertisementPayload { + unsigned char pubKey[32]; + int32_t timestamp; + unsigned char signature[64]; + unsigned char dataFlags; + int32_t latitude; + int32_t longitude; + int16_t rfu1; + int16_t rfu2; + char nodeName[128]; + +} AdvertisementPayload; + +typedef struct ReturnedPathPayload { + unsigned char destinationHash; + unsigned char sourceHash; + uint16_t cipherMAC; + unsigned char pathLen; + +} ReturnedPathPayload; + +typedef struct RequestPayload { + unsigned char destinationHash; + unsigned char sourceHash; + uint16_t cipherMAC; +} RequestPayload; + +typedef struct ResponsePayload { + unsigned char destinationHash; + unsigned char sourceHash; + uint16_t cipherMAC; +} ResponsePayload; + +typedef struct PlainTextMessagePayload { + unsigned char destinationHash; + unsigned char sourceHash; + uint16_t cipherMAC; +} PlainTextMessagePayload; + +typedef struct GroupTextMessage { + unsigned char channelHash; + int32_t timestamp; + unsigned char flags; + unsigned char text[190] +} GroupTextMessage; + +#endif diff --git a/main/sx1262.c b/main/sx1262.c new file mode 100644 index 0000000..ebe18f9 --- /dev/null +++ b/main/sx1262.c @@ -0,0 +1,970 @@ +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include +#include +#include "esp_log.h" +#include "buscfg.h" + +#include "sx1262.h" + +#define TAG "SX1262" + +#define HOST_ID SPI3_HOST + +static spi_device_handle_t SpiHandle; + +// Global Stuff +static uint8_t PacketParams[6]; +static bool txActive; +static int txLost = 0; +static bool debugPrint; +static int SX126x_SPI_SELECT; +static int SX126x_BUSY; + +// Arduino compatible macros +#define delayMicroseconds(us) esp_rom_delay_us(us) +#define delay(ms) esp_rom_delay_us(ms*1000) + + +void LoRaErrorDefault(int error) +{ + if (debugPrint) { + ESP_LOGE(TAG, "LoRaErrorDefault=%d", error); + } + while (true) { + vTaskDelay(1); + } +} + +__attribute__ ((weak, alias ("LoRaErrorDefault"))) void LoRaError(int error); + + +void LoRaInit(void) +{ + ESP_LOGI(TAG, "HSPI_MISO_GPIO=%d", HSPI_MISO_GPIO); + ESP_LOGI(TAG, "HSPI_MOSI_GPIO=%d", HSPI_MOSI_GPIO); + ESP_LOGI(TAG, "HSPI_SCK_GPIO=%d", HSPI_SCK_GPIO); + ESP_LOGI(TAG, "HSPI_LORA_CS=%d", HSPI_LORA_CS); + ESP_LOGI(TAG, "LORA_BUSY=%d", LORA_BUSY); + + SX126x_SPI_SELECT = HSPI_LORA_CS; + SX126x_BUSY = LORA_BUSY; + + txActive = false; + debugPrint = false; + + gpio_reset_pin(LORA_RESET); + gpio_set_direction(LORA_RESET, GPIO_MODE_OUTPUT); + gpio_set_level(LORA_RESET, 1); + + gpio_reset_pin(SX126x_SPI_SELECT); + gpio_set_direction(SX126x_SPI_SELECT, GPIO_MODE_OUTPUT); + gpio_set_level(SX126x_SPI_SELECT, 1); + + gpio_reset_pin(SX126x_BUSY); + gpio_set_direction(SX126x_BUSY, GPIO_MODE_INPUT); + + spi_bus_config_t spi_bus_config = { + .sclk_io_num = HSPI_SCK_GPIO, + .mosi_io_num = HSPI_MOSI_GPIO, + .miso_io_num = HSPI_MISO_GPIO, + .quadwp_io_num = -1, + .quadhd_io_num = -1 + }; + + esp_err_t ret; + spi_device_interface_config_t devcfg = { + .clock_speed_hz = 9000000, + .mode = 0, + .spics_io_num = HSPI_LORA_CS, + .queue_size = 7, + .flags = 0, + .pre_cb = NULL + }; + //spi_device_handle_t handle; + ret = spi_bus_add_device( HOST_ID, &devcfg, &SpiHandle); + ESP_LOGI(TAG, "spi_bus_add_device=%d",ret); + assert(ret==ESP_OK); +} + +void spi_write_byte(uint8_t* Dataout, size_t DataLength ) +{ + spi_transaction_t SPITransaction; + + if ( DataLength > 0 ) { + memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); + SPITransaction.length = DataLength * 8; + SPITransaction.tx_buffer = Dataout; + SPITransaction.rx_buffer = NULL; + spi_device_transmit( SpiHandle, &SPITransaction ); + } + + return; +} + +void spi_read_byte(uint8_t* Datain, uint8_t* Dataout, size_t DataLength ) +{ + spi_transaction_t SPITransaction; + + if ( DataLength > 0 ) { + memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); + SPITransaction.length = DataLength * 8; + SPITransaction.tx_buffer = Dataout; + SPITransaction.rx_buffer = Datain; + spi_device_transmit( SpiHandle, &SPITransaction ); + } + + return; +} + +uint8_t spi_transfer(uint8_t address) +{ + uint8_t datain[1]; + uint8_t dataout[1]; + dataout[0] = address; + //spi_write_byte(dataout, 1 ); + spi_read_byte(datain, dataout, 1 ); + return datain[0]; +} + + +int16_t LoRaBegin(uint32_t frequencyInHz, int8_t txPowerInDbm, float tcxoVoltage, bool useRegulatorLDO) +{ + if ( txPowerInDbm > 22 ) + txPowerInDbm = 22; + if ( txPowerInDbm < -3 ) + txPowerInDbm = -3; + + Reset(); + + uint8_t wk[2]; + ReadRegister(SX126X_REG_LORA_SYNC_WORD_MSB, wk, 2); // 0x0740 + uint16_t syncWord = (wk[0] << 8) + wk[1]; + ESP_LOGI(TAG, "syncWord=0x%x", syncWord); + if (syncWord != SX126X_SYNC_WORD_PUBLIC && syncWord != SX126X_SYNC_WORD_PRIVATE) { + ESP_LOGE(TAG, "SX126x error, maybe no SPI connection"); + return ERR_INVALID_MODE; + } + + ESP_LOGI(TAG, "SX126x installed"); + SetStandby(SX126X_STANDBY_RC); + + SetDio2AsRfSwitchCtrl(true); + ESP_LOGI(TAG, "tcxoVoltage=%f", tcxoVoltage); + // set TCXO control, if requested + if(tcxoVoltage > 0.0) { + SetDio3AsTcxoCtrl(tcxoVoltage, RADIO_TCXO_SETUP_TIME); // Configure the radio to use a TCXO controlled by DIO3 + } + + Calibrate( SX126X_CALIBRATE_IMAGE_ON + | SX126X_CALIBRATE_ADC_BULK_P_ON + | SX126X_CALIBRATE_ADC_BULK_N_ON + | SX126X_CALIBRATE_ADC_PULSE_ON + | SX126X_CALIBRATE_PLL_ON + | SX126X_CALIBRATE_RC13M_ON + | SX126X_CALIBRATE_RC64K_ON + ); + + ESP_LOGI(TAG, "useRegulatorLDO=%d", useRegulatorLDO); + if (useRegulatorLDO) { + SetRegulatorMode(SX126X_REGULATOR_LDO); // set regulator mode: LDO + } else { + SetRegulatorMode(SX126X_REGULATOR_DC_DC); // set regulator mode: DC-DC + } + + SetBufferBaseAddress(0, 0); +#if 0 + // SX1261_TRANCEIVER + SetPaConfig(0x06, 0x00, 0x01, 0x01); // PA Optimal Settings +15 dBm + // SX1262_TRANCEIVER + SetPaConfig(0x04, 0x07, 0x00, 0x01); // PA Optimal Settings +22 dBm + // SX1268_TRANCEIVER + SetPaConfig(0x04, 0x07, 0x00, 0x01); // PA Optimal Settings +22 dBm +#endif + SetPaConfig(0x04, 0x07, 0x00, 0x01); // PA Optimal Settings +22 dBm + SetOvercurrentProtection(60.0); // current max 60mA for the whole device + SetPowerConfig(txPowerInDbm, SX126X_PA_RAMP_200U); //0 fuer Empfaenger + SetRfFrequency(frequencyInHz); + return ERR_NONE; +} + +void FixInvertedIQ(uint8_t iqConfig) +{ + // fixes IQ configuration for inverted IQ + // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details + // When exchanging LoRa packets with inverted IQ polarity, some packet losses may be observed for longer packets. + // Workaround: Bit 2 at address 0x0736 must be set to: + // “0” when using inverted IQ polarity (see the SetPacketParam(...) command) + // “1” when using standard IQ polarity + + // read current IQ configuration + uint8_t iqConfigCurrent = 0; + ReadRegister(SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736 + + // set correct IQ configuration + //if(iqConfig == SX126X_LORA_IQ_STANDARD) { + if(iqConfig == SX126X_LORA_IQ_INVERTED) { + iqConfigCurrent &= 0xFB; // using inverted IQ polarity + } else { + iqConfigCurrent |= 0x04; // using standard IQ polarity + } + + // update with the new value + WriteRegister(SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736 +} + + +void LoRaConfig(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq) +{ + SetStopRxTimerOnPreambleDetect(false); + SetLoRaSymbNumTimeout(0); + SetPacketType(SX126X_PACKET_TYPE_LORA); // SX126x.ModulationParams.PacketType : MODEM_LORA + uint8_t ldro = 0; // LowDataRateOptimize OFF + SetModulationParams(spreadingFactor, bandwidth, codingRate, ldro); + + PacketParams[0] = (preambleLength >> 8) & 0xFF; + PacketParams[1] = preambleLength; + if ( payloadLen ) + { + PacketParams[2] = 0x01; // Fixed length packet (implicit header) + PacketParams[3] = payloadLen; + } + else + { + PacketParams[2] = 0x00; // Variable length packet (explicit header) + PacketParams[3] = 0xFF; + } + + if ( crcOn ) + PacketParams[4] = SX126X_LORA_CRC_ON; + else + PacketParams[4] = SX126X_LORA_CRC_OFF; + + if ( invertIrq ) + PacketParams[5] = 0x01; // Inverted LoRa I and Q signals setup + else + PacketParams[5] = 0x00; // Standard LoRa I and Q signals setup + + // fixes IQ configuration for inverted IQ + FixInvertedIQ(PacketParams[5]); + + WriteCommand(SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C + + // Do not use DIO interruptst + SetDioIrqParams(SX126X_IRQ_ALL, //all interrupts enabled + SX126X_IRQ_NONE, //interrupts on DIO1 + SX126X_IRQ_NONE, //interrupts on DIO2 + SX126X_IRQ_NONE //interrupts on DIO3 + ); + + + ESP_LOGI(TAG, "Almost done setting LoRa"); + // Receive state no receive timeoout + SetRx(0xFFFFFF); +} + + +void LoRaDebugPrint(bool enable) +{ + debugPrint = enable; +} + + +uint8_t LoRaReceive(uint8_t *pData, int16_t len) +{ + uint8_t rxLen = 0; + uint16_t irqRegs = GetIrqStatus(); + //uint8_t status = GetStatus(); + + if( irqRegs & SX126X_IRQ_RX_DONE ) + { + //ClearIrqStatus(SX126X_IRQ_RX_DONE); + ClearIrqStatus(SX126X_IRQ_ALL); + rxLen = ReadBuffer(pData, len); + } + + return rxLen; +} + + +bool LoRaSend(uint8_t *pData, int16_t len, uint8_t mode) +{ + uint16_t irqStatus; + bool rv = false; + + if ( txActive == false ) + { + txActive = true; + if (PacketParams[2] == 0x00) { // Variable length packet (explicit header) + PacketParams[3] = len; + } + WriteCommand(SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C + + //ClearIrqStatus(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT); + ClearIrqStatus(SX126X_IRQ_ALL); + + WriteBuffer(pData, len); + SetTx(500); + + if ( mode & SX126x_TXMODE_SYNC ) + { + irqStatus = GetIrqStatus(); + while ( (!(irqStatus & SX126X_IRQ_TX_DONE)) && (!(irqStatus & SX126X_IRQ_TIMEOUT)) ) + { + delay(1); + irqStatus = GetIrqStatus(); + } + if (debugPrint) { + ESP_LOGI(TAG, "irqStatus=0x%x", irqStatus); + if (irqStatus & SX126X_IRQ_TX_DONE) { + ESP_LOGI(TAG, "SX126X_IRQ_TX_DONE"); + } + if (irqStatus & SX126X_IRQ_TIMEOUT) { + ESP_LOGI(TAG, "SX126X_IRQ_TIMEOUT"); + } + } + txActive = false; + + SetRx(0xFFFFFF); + + if ( irqStatus & SX126X_IRQ_TX_DONE) { + rv = true; + } + } + else + { + rv = true; + } + } + if (debugPrint) { + ESP_LOGI(TAG, "Send rv=0x%x", rv); + } + if (rv == false) txLost++; + return rv; +} + + +bool ReceiveMode(void) +{ + uint16_t irq; + bool rv = false; + + if ( txActive == false ) + { + rv = true; + } + else + { + irq = GetIrqStatus(); + if ( irq & (SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT) ) + { + SetRx(0xFFFFFF); + txActive = false; + rv = true; + } + } + + return rv; +} + + +void GetPacketStatus(int8_t *rssiPacket, int8_t *snrPacket) +{ + uint8_t buf[4]; + ReadCommand( SX126X_CMD_GET_PACKET_STATUS, buf, 4 ); // 0x14 + *rssiPacket = (buf[3] >> 1) * -1; + ( buf[2] < 128 ) ? ( *snrPacket = buf[2] >> 2 ) : ( *snrPacket = ( ( buf[2] - 256 ) >> 2 ) ); +} + + +void SetTxPower(int8_t txPowerInDbm) +{ + SetPowerConfig(txPowerInDbm, SX126X_PA_RAMP_200U); +} + + +void Reset(void) +{ + delay(10); + gpio_set_level(LORA_RESET, 0); + delay(20); + gpio_set_level(LORA_RESET, 1); + delay(10); + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "Reset", true); +} + + +void Wakeup(void) +{ + GetStatus(); +} + + +void SetStandby(uint8_t mode) +{ + uint8_t data = mode; + WriteCommand(SX126X_CMD_SET_STANDBY, &data, 1); // 0x80 +} + + +uint8_t GetStatus(void) +{ + uint8_t rv; + ReadCommand(SX126X_CMD_GET_STATUS, &rv, 1); // 0xC0 + return rv; +} + + +void SetDio3AsTcxoCtrl(float voltage, uint32_t delay) +{ + uint8_t buf[4]; + + //buf[0] = tcxoVoltage & 0x07; + if(fabs(voltage - 1.6) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_1_6; + } else if(fabs(voltage - 1.7) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_1_7; + } else if(fabs(voltage - 1.8) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_1_8; + } else if(fabs(voltage - 2.2) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_2_2; + } else if(fabs(voltage - 2.4) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_2_4; + } else if(fabs(voltage - 2.7) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_2_7; + } else if(fabs(voltage - 3.0) <= 0.001) { + buf[0] = SX126X_DIO3_OUTPUT_3_0; + } else { + buf[0] = SX126X_DIO3_OUTPUT_3_3; + } + + uint32_t delayValue = (float)delay / 15.625; + buf[1] = ( uint8_t )( ( delayValue >> 16 ) & 0xFF ); + buf[2] = ( uint8_t )( ( delayValue >> 8 ) & 0xFF ); + buf[3] = ( uint8_t )( delayValue & 0xFF ); + + WriteCommand(SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, buf, 4); // 0x97 +} + + +void Calibrate(uint8_t calibParam) +{ + uint8_t data = calibParam; + WriteCommand(SX126X_CMD_CALIBRATE, &data, 1); // 0x89 +} + + +void SetDio2AsRfSwitchCtrl(uint8_t enable) +{ + uint8_t data = enable; + WriteCommand(SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1); // 0x9D +} + + +void SetRfFrequency(uint32_t frequency) +{ + uint8_t buf[4]; + uint32_t freq = 0; + + CalibrateImage(frequency); + + freq = (uint32_t)((double)frequency / (double)FREQ_STEP); + buf[0] = (uint8_t)((freq >> 24) & 0xFF); + buf[1] = (uint8_t)((freq >> 16) & 0xFF); + buf[2] = (uint8_t)((freq >> 8) & 0xFF); + buf[3] = (uint8_t)(freq & 0xFF); + WriteCommand(SX126X_CMD_SET_RF_FREQUENCY, buf, 4); // 0x86 +} + + +void CalibrateImage(uint32_t frequency) +{ + uint8_t calFreq[2]; + + if( frequency> 900000000 ) + { + calFreq[0] = 0xE1; + calFreq[1] = 0xE9; + } + else if( frequency > 850000000 ) + { + calFreq[0] = 0xD7; + calFreq[1] = 0xDB; + } + else if( frequency > 770000000 ) + { + calFreq[0] = 0xC1; + calFreq[1] = 0xC5; + } + else if( frequency > 460000000 ) + { + calFreq[0] = 0x75; + calFreq[1] = 0x81; + } + else if( frequency > 425000000 ) + { + calFreq[0] = 0x6B; + calFreq[1] = 0x6F; + } + WriteCommand(SX126X_CMD_CALIBRATE_IMAGE, calFreq, 2); // 0x98 +} + + +void SetRegulatorMode(uint8_t mode) +{ + uint8_t data = mode; + WriteCommand(SX126X_CMD_SET_REGULATOR_MODE, &data, 1); // 0x96 +} + + +void SetBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) +{ + uint8_t buf[2]; + + buf[0] = txBaseAddress; + buf[1] = rxBaseAddress; + WriteCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, buf, 2); // 0x8F +} + + +void SetPowerConfig(int8_t power, uint8_t rampTime) +{ + uint8_t buf[2]; + + if( power > 22 ) + { + power = 22; + } + else if( power < -3 ) + { + power = -3; + } + + buf[0] = power; + buf[1] = ( uint8_t )rampTime; + WriteCommand(SX126X_CMD_SET_TX_PARAMS, buf, 2); // 0x8E +} + + +void SetPaConfig(uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut) +{ + uint8_t buf[4]; + + buf[0] = paDutyCycle; + buf[1] = hpMax; + buf[2] = deviceSel; + buf[3] = paLut; + WriteCommand(SX126X_CMD_SET_PA_CONFIG, buf, 4); // 0x95 +} + + +void SetOvercurrentProtection(float currentLimit) +{ + if((currentLimit >= 0.0) && (currentLimit <= 140.0)) { + uint8_t buf[1]; + buf[0] = (uint8_t)(currentLimit / 2.5); + WriteRegister(SX126X_REG_OCP_CONFIGURATION, buf, 1); // 0x08E7 + } +} + +void SetSyncWord(int16_t sync) { + uint8_t buf[2]; + + buf[0] = (uint8_t)((sync >> 8) & 0x00FF); + buf[1] = (uint8_t)(sync & 0x00FF); + WriteRegister(SX126X_REG_LORA_SYNC_WORD_MSB, buf, 2); // 0x0740 +} + +void SetDioIrqParams +( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ) +{ + uint8_t buf[8]; + + buf[0] = (uint8_t)((irqMask >> 8) & 0x00FF); + buf[1] = (uint8_t)(irqMask & 0x00FF); + buf[2] = (uint8_t)((dio1Mask >> 8) & 0x00FF); + buf[3] = (uint8_t)(dio1Mask & 0x00FF); + buf[4] = (uint8_t)((dio2Mask >> 8) & 0x00FF); + buf[5] = (uint8_t)(dio2Mask & 0x00FF); + buf[6] = (uint8_t)((dio3Mask >> 8) & 0x00FF); + buf[7] = (uint8_t)(dio3Mask & 0x00FF); + WriteCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, buf, 8); // 0x08 +} + + +void SetStopRxTimerOnPreambleDetect(bool enable) +{ + ESP_LOGI(TAG, "SetStopRxTimerOnPreambleDetect enable=%d", enable); + //uint8_t data = (uint8_t)enable; + uint8_t data = 0; + if (enable) data = 1; + WriteCommand(SX126X_CMD_STOP_TIMER_ON_PREAMBLE, &data, 1); // 0x9F +} + + +void SetLoRaSymbNumTimeout(uint8_t SymbNum) +{ + uint8_t data = SymbNum; + WriteCommand(SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT, &data, 1); // 0xA0 +} + + +void SetPacketType(uint8_t packetType) +{ + uint8_t data = packetType; + WriteCommand(SX126X_CMD_SET_PACKET_TYPE, &data, 1); // 0x01 +} + + +void SetModulationParams(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint8_t lowDataRateOptimize) +{ + uint8_t data[4]; + //currently only LoRa supported + data[0] = spreadingFactor; + data[1] = bandwidth; + data[2] = codingRate; + data[3] = lowDataRateOptimize; + WriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 4); // 0x8B +} + + +void SetCadParams(uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout) +{ + uint8_t data[7]; + data[0] = cadSymbolNum; + data[1] = cadDetPeak; + data[2] = cadDetMin; + data[3] = cadExitMode; + data[4] = (uint8_t)((cadTimeout >> 16) & 0xFF); + data[5] = (uint8_t)((cadTimeout >> 8) & 0xFF); + data[6] = (uint8_t)(cadTimeout & 0xFF); + WriteCommand(SX126X_CMD_SET_CAD_PARAMS, data, 7); // 0x88 +} + + +void SetCad() +{ + uint8_t data = 0; + WriteCommand(SX126X_CMD_SET_CAD, &data, 0); // 0xC5 +} + + +uint16_t GetIrqStatus( void ) +{ + uint8_t data[3]; + ReadCommand(SX126X_CMD_GET_IRQ_STATUS, data, 3); // 0x12 + return (data[1] << 8) | data[2]; +} + + +void ClearIrqStatus(uint16_t irq) +{ + uint8_t buf[2]; + + buf[0] = (uint8_t)(((uint16_t)irq >> 8) & 0x00FF); + buf[1] = (uint8_t)((uint16_t)irq & 0x00FF); + WriteCommand(SX126X_CMD_CLEAR_IRQ_STATUS, buf, 2); // 0x02 +} + + +void SetRx(uint32_t timeout) +{ + if (debugPrint) { + ESP_LOGI(TAG, "----- SetRx timeout=%"PRIu32, timeout); + } + SetStandby(SX126X_STANDBY_RC); + uint8_t buf[3]; + buf[0] = (uint8_t)((timeout >> 16) & 0xFF); + buf[1] = (uint8_t)((timeout >> 8) & 0xFF); + buf[2] = (uint8_t)(timeout & 0xFF); + WriteCommand(SX126X_CMD_SET_RX, buf, 3); // 0x82 + + for(int retry=0;retry<10;retry++) { + if ((GetStatus() & 0x70) == 0x50) break; + delay(1); + } + if ((GetStatus() & 0x70) != 0x50) { + ESP_LOGE(TAG, "SetRx Illegal Status"); + LoRaError(ERR_INVALID_SETRX_STATE); + } +} + + +void SetTx(uint32_t timeoutInMs) +{ + if (debugPrint) { + ESP_LOGI(TAG, "----- SetTx timeoutInMs=%"PRIu32, timeoutInMs); + } + SetStandby(SX126X_STANDBY_RC); + uint8_t buf[3]; + uint32_t tout = timeoutInMs; + if (timeoutInMs != 0) { + uint32_t timeoutInUs = timeoutInMs * 1000; + tout = (uint32_t)(timeoutInUs / 0.015625); + } + if (debugPrint) { + ESP_LOGI(TAG, "SetTx timeoutInMs=%"PRIu32" tout=%"PRIu32, timeoutInMs, tout); + } + buf[0] = (uint8_t)((tout >> 16) & 0xFF); + buf[1] = (uint8_t)((tout >> 8) & 0xFF); + buf[2] = (uint8_t )(tout & 0xFF); + WriteCommand(SX126X_CMD_SET_TX, buf, 3); // 0x83 + + for(int retry=0;retry<10;retry++) { + if ((GetStatus() & 0x70) == 0x60) break; + vTaskDelay(1); + } + if ((GetStatus() & 0x70) != 0x60) { + ESP_LOGE(TAG, "SetTx Illegal Status"); + LoRaError(ERR_INVALID_SETTX_STATE); + } +} + +int GetPacketLost() +{ + return txLost; +} + + +uint8_t GetRssiInst() +{ + uint8_t buf[2]; + ReadCommand( SX126X_CMD_GET_RSSI_INST, buf, 2 ); // 0x15 + return buf[1]; +} + + +void GetRxBufferStatus(uint8_t *payloadLength, uint8_t *rxStartBufferPointer) +{ + uint8_t buf[3]; + ReadCommand( SX126X_CMD_GET_RX_BUFFER_STATUS, buf, 3 ); // 0x13 + *payloadLength = buf[1]; + *rxStartBufferPointer = buf[2]; +} + + +void WaitForIdleBegin(unsigned long timeout, char *text) { + // ensure BUSY is low (state meachine ready) + bool stop = false; + for (int retry=0;retry<10;retry++) { + if (retry == 9) stop = true; + bool ret = WaitForIdle(BUSY_WAIT, text, stop); + if (ret == true) break; + ESP_LOGW(TAG, "WaitForIdle fail retry=%d", retry); + vTaskDelay(1); + } +} + + +bool WaitForIdle(unsigned long timeout, char *text, bool stop) +{ + bool ret = true; + TickType_t start = xTaskGetTickCount(); + delayMicroseconds(1); + while(xTaskGetTickCount() - start < (timeout/portTICK_PERIOD_MS)) { + if (gpio_get_level(SX126x_BUSY) == 0) break; + delayMicroseconds(1); + } + if (gpio_get_level(SX126x_BUSY)) { + if (stop) { + ESP_LOGE(TAG, "WaitForIdle Timeout text=%s timeout=%lu start=%"PRIu32, text, timeout, start); + LoRaError(ERR_IDLE_TIMEOUT); + } else { + ESP_LOGW(TAG, "WaitForIdle Timeout text=%s timeout=%lu start=%"PRIu32, text, timeout, start); + ret = false; + } + } + return ret; +} + + +uint8_t ReadBuffer(uint8_t *rxData, int16_t rxDataLen) +{ + uint8_t offset = 0; + uint8_t payloadLength = 0; + GetRxBufferStatus(&payloadLength, &offset); + if( payloadLength > rxDataLen ) + { + ESP_LOGW(TAG, "ReadBuffer rxDataLen too small. payloadLength=%d rxDataLen=%d", payloadLength, rxDataLen); + return 0; + } + + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "start ReadBuffer", true); + + // start transfer + uint8_t *buf; + buf = malloc(payloadLength+3); + if (buf != NULL) { + buf[0] = SX126X_CMD_READ_BUFFER; // 0x1E + buf[1] = offset; // offset in rx fifo + buf[2] = SX126X_CMD_NOP; + memset(&buf[3], SX126X_CMD_NOP, payloadLength); + spi_read_byte(buf, buf, payloadLength+3); + memcpy(rxData, &buf[3], payloadLength); + free(buf); + } else { + ESP_LOGE(TAG, "ReadBuffer malloc fail"); + payloadLength = 0; + } + + // wait for BUSY to go low + WaitForIdle(BUSY_WAIT, "end ReadBuffer", false); + + return payloadLength; +} + + +void WriteBuffer(uint8_t *txData, int16_t txDataLen) +{ + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "start WriteBuffer", true); + + // start transfer + uint8_t *buf; + buf = malloc(txDataLen+2); + if (buf != NULL) { + buf[0] = SX126X_CMD_WRITE_BUFFER; // 0x0E + buf[1] = 0; // offset in tx fifo + memcpy(&buf[2], txData, txDataLen); + spi_write_byte(buf, txDataLen+2); + free(buf); + } else { + ESP_LOGE(TAG, "WriteBuffer malloc fail"); + } + + // wait for BUSY to go low + WaitForIdle(BUSY_WAIT, "end WriteBuffer", false); +} + + +void WriteRegister(uint16_t reg, uint8_t* data, uint8_t numBytes) { + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "start WriteRegister", true); + + if(debugPrint) { + ESP_LOGI(TAG, "WriteRegister: REG=0x%02x", reg); + for(uint8_t n = 0; n < numBytes; n++) { + ESP_LOGI(TAG, "DataOut:%02x ", data[n]); + } + } + + // start transfer + uint8_t buf[16]; + buf[0] = SX126X_CMD_WRITE_REGISTER; + buf[1] = (reg & 0xFF00) >> 8; + buf[2] = reg & 0xff; + memcpy(&buf[3], data, numBytes); + spi_write_byte(buf, 3 + numBytes); + + // wait for BUSY to go low + WaitForIdle(BUSY_WAIT, "end WriteRegister", false); +} + + +void ReadRegister(uint16_t reg, uint8_t* data, uint8_t numBytes) { + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "start ReadRegister", true); + + if(debugPrint) { + ESP_LOGI(TAG, "ReadRegister: REG=0x%02x", reg); + } + + // start transfer + uint8_t buf[16]; + memset(buf, SX126X_CMD_NOP, sizeof(buf)); + buf[0] = SX126X_CMD_READ_REGISTER; + buf[1] = (reg & 0xFF00) >> 8; + buf[2] = reg & 0xff; + spi_read_byte(buf, buf, 4 + numBytes); + memcpy(data, &buf[4], numBytes); + if(debugPrint) { + for(uint8_t n = 0; n < numBytes; n++) { + ESP_LOGI(TAG, "DataIn:%02x ", data[n]); + } + } + + // wait for BUSY to go low + WaitForIdle(BUSY_WAIT, "end ReadRegister", false); +} + +// WriteCommand with retry +void WriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes) { + uint8_t status; + for (int retry=1; retry<10; retry++) { + status = WriteCommand2(cmd, data, numBytes); + ESP_LOGD(TAG, "status=%02x", status); + if (status == 0) break; + ESP_LOGW(TAG, "WriteCommand2 status=%02x retry=%d", status, retry); + } + if (status != 0) { + ESP_LOGE(TAG, "SPI Transaction error:0x%02x", status); + LoRaError(ERR_SPI_TRANSACTION); + } +} + +uint8_t WriteCommand2(uint8_t cmd, uint8_t* data, uint8_t numBytes) { + // ensure BUSY is low (state meachine ready) + WaitForIdle(BUSY_WAIT, "start WriteCommand2", true); + + if(debugPrint) { + ESP_LOGI(TAG, "WriteCommand: CMD=0x%02x", cmd); + } + + // start transfer + uint8_t buf[16]; + buf[0] = cmd; + memcpy(&buf[1], data, numBytes); + spi_read_byte(buf, buf, numBytes + 1); + + uint8_t status = 0; + uint8_t cmd_status = buf[1] & 0xe; + + switch(cmd_status){ + case SX126X_STATUS_CMD_TIMEOUT: + case SX126X_STATUS_CMD_INVALID: + case SX126X_STATUS_CMD_FAILED: + status = cmd_status; + break; + + case 0: + case 7: + status = SX126X_STATUS_SPI_FAILED; + break; + // default: break; // success + } + + // wait for BUSY to go low + WaitForIdle(BUSY_WAIT, "end WriteCommand2", false); + return status; +} + + +void ReadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes) { + // ensure BUSY is low (state meachine ready) + WaitForIdleBegin(BUSY_WAIT, "start ReadCommand"); + + if(debugPrint) { + ESP_LOGI(TAG, "ReadCommand: CMD=0x%02x", cmd); + } + + // start transfer + uint8_t buf[16]; + memset(buf, SX126X_CMD_NOP, sizeof(buf)); + buf[0] = cmd; + spi_read_byte(buf, buf, 1 + numBytes); + if (data != NULL && numBytes) + memcpy(data, &buf[1], numBytes); + + // wait for BUSY to go low + vTaskDelay(1); + WaitForIdle(BUSY_WAIT, "end ReadCommand", false); +} \ No newline at end of file diff --git a/main/sx1262.h b/main/sx1262.h new file mode 100644 index 0000000..efb92dc --- /dev/null +++ b/main/sx1262.h @@ -0,0 +1,435 @@ +#ifndef _RA01S_H +#define _RA01S_H + +#include "driver/spi_master.h" + +//return values +#define ERR_NONE 0 +#define ERR_PACKET_TOO_LONG 1 +#define ERR_UNKNOWN 2 +#define ERR_TX_TIMEOUT 3 +#define ERR_RX_TIMEOUT 4 +#define ERR_CRC_MISMATCH 5 +#define ERR_WRONG_MODEM 6 +#define ERR_INVALID_BANDWIDTH 7 +#define ERR_INVALID_SPREADING_FACTOR 8 +#define ERR_INVALID_CODING_RATE 9 +#define ERR_INVALID_FREQUENCY_DEVIATION 10 +#define ERR_INVALID_BIT_RATE 11 +#define ERR_INVALID_RX_BANDWIDTH 12 +#define ERR_INVALID_DATA_SHAPING 13 +#define ERR_INVALID_SYNC_WORD 14 +#define ERR_INVALID_OUTPUT_POWER 15 +#define ERR_INVALID_MODE 16 +#define ERR_INVALID_TRANCEIVER 17 +#define ERR_INVALID_SETRX_STATE 18 +#define ERR_INVALID_SETTX_STATE 19 +#define ERR_IDLE_TIMEOUT 20 +#define ERR_SPI_TRANSACTION 21 + +// SX126X physical layer properties +#define XTAL_FREQ ( double )32000000 +#define FREQ_DIV ( double )pow( 2.0, 25.0 ) +#define FREQ_STEP ( double )( XTAL_FREQ / FREQ_DIV ) + +#define LOW 0 +#define HIGH 1 +#define BUSY_WAIT 5000 + +// SX126X Model +#define SX1261_TRANCEIVER 0x01 +#define SX1262_TRANCEIVER 0x02 +#define SX1268_TRANCEIVER 0x08 + +// SX126X SPI commands +// operational modes commands +#define SX126X_CMD_NOP 0x00 +#define SX126X_CMD_SET_SLEEP 0x84 +#define SX126X_CMD_SET_STANDBY 0x80 +#define SX126X_CMD_SET_FS 0xC1 +#define SX126X_CMD_SET_TX 0x83 +#define SX126X_CMD_SET_RX 0x82 +#define SX126X_CMD_STOP_TIMER_ON_PREAMBLE 0x9F +#define SX126X_CMD_SET_RX_DUTY_CYCLE 0x94 +#define SX126X_CMD_SET_CAD 0xC5 +#define SX126X_CMD_SET_TX_CONTINUOUS_WAVE 0xD1 +#define SX126X_CMD_SET_TX_INFINITE_PREAMBLE 0xD2 +#define SX126X_CMD_SET_REGULATOR_MODE 0x96 +#define SX126X_CMD_CALIBRATE 0x89 +#define SX126X_CMD_CALIBRATE_IMAGE 0x98 +#define SX126X_CMD_SET_PA_CONFIG 0x95 +#define SX126X_CMD_SET_RX_TX_FALLBACK_MODE 0x93 + +// register and buffer access commands +#define SX126X_CMD_WRITE_REGISTER 0x0D +#define SX126X_CMD_READ_REGISTER 0x1D +#define SX126X_CMD_WRITE_BUFFER 0x0E +#define SX126X_CMD_READ_BUFFER 0x1E + +// DIO and IRQ control +#define SX126X_CMD_SET_DIO_IRQ_PARAMS 0x08 +#define SX126X_CMD_GET_IRQ_STATUS 0x12 +#define SX126X_CMD_CLEAR_IRQ_STATUS 0x02 +#define SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL 0x9D +#define SX126X_CMD_SET_DIO3_AS_TCXO_CTRL 0x97 + +// RF, modulation and packet commands +#define SX126X_CMD_SET_RF_FREQUENCY 0x86 +#define SX126X_CMD_SET_PACKET_TYPE 0x8A +#define SX126X_CMD_GET_PACKET_TYPE 0x11 +#define SX126X_CMD_SET_TX_PARAMS 0x8E +#define SX126X_CMD_SET_MODULATION_PARAMS 0x8B +#define SX126X_CMD_SET_PACKET_PARAMS 0x8C +#define SX126X_CMD_SET_CAD_PARAMS 0x88 +#define SX126X_CMD_SET_BUFFER_BASE_ADDRESS 0x8F +#define SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT 0xA0 + +#define SX126X_PA_CONFIG_SX1261 0x01 +#define SX126X_PA_CONFIG_SX1262 0x00 + +// status commands +#define SX126X_CMD_GET_STATUS 0xC0 +#define SX126X_CMD_GET_RSSI_INST 0x15 +#define SX126X_CMD_GET_RX_BUFFER_STATUS 0x13 +#define SX126X_CMD_GET_PACKET_STATUS 0x14 +#define SX126X_CMD_GET_DEVICE_ERRORS 0x17 +#define SX126X_CMD_CLEAR_DEVICE_ERRORS 0x07 +#define SX126X_CMD_GET_STATS 0x10 +#define SX126X_CMD_RESET_STATS 0x00 + + +// SX126X register map +#define SX126X_REG_HOPPING_ENABLE 0x0385 +#define SX126X_REG_PACKECT_LENGTH 0x0386 +#define SX126X_REG_NB_HOPPING_BLOCKS 0x0387 +#define SX126X_REG_NB_SYMBOLS0 0x0388 +#define SX126X_REG_FREQ0 0x038A +#define SX126X_REG_NB_SYMBOLS15 0x03E2 +#define SX126X_REG_FREQ15 0x03E4 +#define SX126X_REG_DIOX_OUTPUT_ENABLE 0x0580 +#define SX126X_REG_DIOX_INPUT_ENABLE 0x0583 +#define SX126X_REG_DIOX_PILL_UP_CONTROL 0x0584 +#define SX126X_REG_DIOX_PULL_DOWN_CONTROL 0x0585 +#define SX126X_REG_WHITENING_INITIAL_MSB 0x06B8 +#define SX126X_REG_WHITENING_INITIAL_LSB 0x06B9 +#define SX126X_REG_CRC_INITIAL_MSB 0x06BC +#define SX126X_REG_CRC_INITIAL_LSB 0x06BD +#define SX126X_REG_CRC_POLYNOMIAL_MSB 0x06BE +#define SX126X_REG_CRC_POLYNOMIAL_LSB 0x06BF +#define SX126X_REG_SYNC_WORD_0 0x06C0 +#define SX126X_REG_SYNC_WORD_1 0x06C1 +#define SX126X_REG_SYNC_WORD_2 0x06C2 +#define SX126X_REG_SYNC_WORD_3 0x06C3 +#define SX126X_REG_SYNC_WORD_4 0x06C4 +#define SX126X_REG_SYNC_WORD_5 0x06C5 +#define SX126X_REG_SYNC_WORD_6 0x06C6 +#define SX126X_REG_SYNC_WORD_7 0x06C7 +#define SX126X_REG_NODE_ADDRESS 0x06CD +#define SX126X_REG_BROADCAST_ADDRESS 0x06CE +#define SX126X_REG_IQ_POLARITY_SETUP 0x0736 +#define SX126X_REG_LORA_SYNC_WORD_MSB 0x0740 +#define SX126X_REG_LORA_SYNC_WORD_LSB 0x0741 +#define SX126X_REG_RANDOM_NUMBER_0 0x0819 +#define SX126X_REG_RANDOM_NUMBER_1 0x081A +#define SX126X_REG_RANDOM_NUMBER_2 0x081B +#define SX126X_REG_RANDOM_NUMBER_3 0x081C +#define SX126X_REG_TX_MODULETION 0x0889 +#define SX126X_REG_RX_GAIN 0x08AC +#define SX126X_REG_TX_CLAMP_CONFIG 0x08D8 +#define SX126X_REG_OCP_CONFIGURATION 0x08E7 +#define SX126X_REG_RTC_CONTROL 0x0902 +#define SX126X_REG_XTA_TRIM 0x0911 +#define SX126X_REG_XTB_TRIM 0x0912 +#define SX126X_REG_DIO3_OUTPUT_VOLTAGE_CONTROL 0x0920 +#define SX126X_REG_EVENT_MASK 0x0944 + + +// SX126X SPI command variables +//SX126X_CMD_SET_SLEEP +#define SX126X_SLEEP_START_COLD 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default) +#define SX126X_SLEEP_START_WARM 0b00000100 // 2 2 warm start, configuration is retained +#define SX126X_SLEEP_RTC_OFF 0b00000000 // 0 0 wake on RTC timeout: disabled +#define SX126X_SLEEP_RTC_ON 0b00000001 // 0 0 enabled + +//SX126X_CMD_SET_STANDBY +#define SX126X_STANDBY_RC 0x00 // 7 0 standby mode: 13 MHz RC oscillator +#define SX126X_STANDBY_XOSC 0x01 // 7 0 32 MHz crystal oscillator + +//SX126X_CMD_SET_RX +#define SX126X_RX_TIMEOUT_NONE 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode) +#define SX126X_RX_TIMEOUT_INF 0xFFFFFF // 23 0 infinite (Rx continuous mode) + +//SX126X_CMD_STOP_TIMER_ON_PREAMBLE +#define SX126X_STOP_ON_PREAMBLE_OFF 0x00 // 7 0 stop timer on: sync word or header (default) +#define SX126X_STOP_ON_PREAMBLE_ON 0x01 // 7 0 preamble detection + +//SX126X_CMD_SET_REGULATOR_MODE +#define SX126X_REGULATOR_LDO 0x00 // 7 0 set regulator mode: LDO (default) +#define SX126X_REGULATOR_DC_DC 0x01 // 7 0 DC-DC + +//SX126X_CMD_CALIBRATE +#define SX126X_CALIBRATE_IMAGE_OFF 0b00000000 // 6 6 image calibration: disabled +#define SX126X_CALIBRATE_IMAGE_ON 0b01000000 // 6 6 enabled +#define SX126X_CALIBRATE_ADC_BULK_P_OFF 0b00000000 // 5 5 ADC bulk P calibration: disabled +#define SX126X_CALIBRATE_ADC_BULK_P_ON 0b00100000 // 5 5 enabled +#define SX126X_CALIBRATE_ADC_BULK_N_OFF 0b00000000 // 4 4 ADC bulk N calibration: disabled +#define SX126X_CALIBRATE_ADC_BULK_N_ON 0b00010000 // 4 4 enabled +#define SX126X_CALIBRATE_ADC_PULSE_OFF 0b00000000 // 3 3 ADC pulse calibration: disabled +#define SX126X_CALIBRATE_ADC_PULSE_ON 0b00001000 // 3 3 enabled +#define SX126X_CALIBRATE_PLL_OFF 0b00000000 // 2 2 PLL calibration: disabled +#define SX126X_CALIBRATE_PLL_ON 0b00000100 // 2 2 enabled +#define SX126X_CALIBRATE_RC13M_OFF 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled +#define SX126X_CALIBRATE_RC13M_ON 0b00000010 // 1 1 enabled +#define SX126X_CALIBRATE_RC64K_OFF 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled +#define SX126X_CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled + +//SX126X_CMD_CALIBRATE_IMAGE +#define SX126X_CAL_IMG_430_MHZ_1 0x6B +#define SX126X_CAL_IMG_430_MHZ_2 0x6F +#define SX126X_CAL_IMG_470_MHZ_1 0x75 +#define SX126X_CAL_IMG_470_MHZ_2 0x81 +#define SX126X_CAL_IMG_779_MHZ_1 0xC1 +#define SX126X_CAL_IMG_779_MHZ_2 0xC5 +#define SX126X_CAL_IMG_863_MHZ_1 0xD7 +#define SX126X_CAL_IMG_863_MHZ_2 0xDB +#define SX126X_CAL_IMG_902_MHZ_1 0xE1 +#define SX126X_CAL_IMG_902_MHZ_2 0xE9 + +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_HP_MAX 0x07 +#define SX126X_PA_CONFIG_SX1268 0x01 +#define SX126X_PA_CONFIG_PA_LUT 0x01 + +//SX126X_CMD_SET_RX_TX_FALLBACK_MODE +#define SX126X_RX_TX_FALLBACK_MODE_FS 0x40 // 7 0 after Rx/Tx go to: FS mode +#define SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC 0x30 // 7 0 standby with crystal oscillator +#define SX126X_RX_TX_FALLBACK_MODE_STDBY_RC 0x20 // 7 0 standby with RC oscillator (default) + +//SX126X_CMD_SET_DIO_IRQ_PARAMS +#define SX126X_IRQ_TIMEOUT 0b1000000000 // 9 9 Rx or Tx timeout +#define SX126X_IRQ_CAD_DETECTED 0b0100000000 // 8 8 channel activity detected +#define SX126X_IRQ_CAD_DONE 0b0010000000 // 7 7 channel activity detection finished +#define SX126X_IRQ_CRC_ERR 0b0001000000 // 6 6 wrong CRC received +#define SX126X_IRQ_HEADER_ERR 0b0000100000 // 5 5 LoRa header CRC error +#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received +#define SX126X_IRQ_SYNC_WORD_VALID 0b0000001000 // 3 3 valid sync word detected +#define SX126X_IRQ_PREAMBLE_DETECTED 0b0000000100 // 2 2 preamble detected +#define SX126X_IRQ_RX_DONE 0b0000000010 // 1 1 packet received +#define SX126X_IRQ_TX_DONE 0b0000000001 // 0 0 packet transmission completed +#define SX126X_IRQ_ALL 0b1111111111 // 9 0 all interrupts +#define SX126X_IRQ_NONE 0b0000000000 // 9 0 no interrupts + +//SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL +#define SX126X_DIO2_AS_IRQ 0x00 // 7 0 DIO2 configuration: IRQ +#define SX126X_DIO2_AS_RF_SWITCH 0x01 // 7 0 RF switch control + +//SX126X_CMD_SET_DIO3_AS_TCXO_CTRL +#define SX126X_DIO3_OUTPUT_1_6 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V +#define SX126X_DIO3_OUTPUT_1_7 0x01 // 7 0 1.7 V +#define SX126X_DIO3_OUTPUT_1_8 0x02 // 7 0 1.8 V +#define SX126X_DIO3_OUTPUT_2_2 0x03 // 7 0 2.2 V +#define SX126X_DIO3_OUTPUT_2_4 0x04 // 7 0 2.4 V +#define SX126X_DIO3_OUTPUT_2_7 0x05 // 7 0 2.7 V +#define SX126X_DIO3_OUTPUT_3_0 0x06 // 7 0 3.0 V +#define SX126X_DIO3_OUTPUT_3_3 0x07 // 7 0 3.3 V + +//Radio complete Wake-up Time with TCXO stabilisation time +#define RADIO_TCXO_SETUP_TIME 5000 // [us] + +//SX126X_CMD_SET_PACKET_TYPE +#define SX126X_PACKET_TYPE_GFSK 0x00 // 7 0 packet type: GFSK +#define SX126X_PACKET_TYPE_LORA 0x01 // 7 0 LoRa + +//SX126X_CMD_SET_TX_PARAMS +#define SX126X_PA_RAMP_10U 0x00 // 7 0 ramp time: 10 us +#define SX126X_PA_RAMP_20U 0x01 // 7 0 20 us +#define SX126X_PA_RAMP_40U 0x02 // 7 0 40 us +#define SX126X_PA_RAMP_80U 0x03 // 7 0 80 us +#define SX126X_PA_RAMP_200U 0x04 // 7 0 200 us +#define SX126X_PA_RAMP_800U 0x05 // 7 0 800 us +#define SX126X_PA_RAMP_1700U 0x06 // 7 0 1700 us +#define SX126X_PA_RAMP_3400U 0x07 // 7 0 3400 us + +//SX126X_CMD_SET_MODULATION_PARAMS +#define SX126X_GFSK_FILTER_NONE 0x00 // 7 0 GFSK filter: none +#define SX126X_GFSK_FILTER_GAUSS_0_3 0x08 // 7 0 Gaussian, BT = 0.3 +#define SX126X_GFSK_FILTER_GAUSS_0_5 0x09 // 7 0 Gaussian, BT = 0.5 +#define SX126X_GFSK_FILTER_GAUSS_0_7 0x0A // 7 0 Gaussian, BT = 0.7 +#define SX126X_GFSK_FILTER_GAUSS_1 0x0B // 7 0 Gaussian, BT = 1 +#define SX126X_GFSK_RX_BW_4_8 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz +#define SX126X_GFSK_RX_BW_5_8 0x17 // 7 0 5.8 kHz +#define SX126X_GFSK_RX_BW_7_3 0x0F // 7 0 7.3 kHz +#define SX126X_GFSK_RX_BW_9_7 0x1E // 7 0 9.7 kHz +#define SX126X_GFSK_RX_BW_11_7 0x16 // 7 0 11.7 kHz +#define SX126X_GFSK_RX_BW_14_6 0x0E // 7 0 14.6 kHz +#define SX126X_GFSK_RX_BW_19_5 0x1D // 7 0 19.5 kHz +#define SX126X_GFSK_RX_BW_23_4 0x15 // 7 0 23.4 kHz +#define SX126X_GFSK_RX_BW_29_3 0x0D // 7 0 29.3 kHz +#define SX126X_GFSK_RX_BW_39_0 0x1C // 7 0 39.0 kHz +#define SX126X_GFSK_RX_BW_46_9 0x14 // 7 0 46.9 kHz +#define SX126X_GFSK_RX_BW_58_6 0x0C // 7 0 58.6 kHz +#define SX126X_GFSK_RX_BW_78_2 0x1B // 7 0 78.2 kHz +#define SX126X_GFSK_RX_BW_93_8 0x13 // 7 0 93.8 kHz +#define SX126X_GFSK_RX_BW_117_3 0x0B // 7 0 117.3 kHz +#define SX126X_GFSK_RX_BW_156_2 0x1A // 7 0 156.2 kHz +#define SX126X_GFSK_RX_BW_187_2 0x12 // 7 0 187.2 kHz +#define SX126X_GFSK_RX_BW_234_3 0x0A // 7 0 234.3 kHz +#define SX126X_GFSK_RX_BW_312_0 0x19 // 7 0 312.0 kHz +#define SX126X_GFSK_RX_BW_373_6 0x11 // 7 0 373.6 kHz +#define SX126X_GFSK_RX_BW_467_0 0x09 // 7 0 467.0 kHz +#define SX126X_LORA_BW_7_8 0x00 // 7 0 LoRa bandwidth: 7.8 kHz +#define SX126X_LORA_BW_10_4 0x08 // 7 0 10.4 kHz +#define SX126X_LORA_BW_15_6 0x01 // 7 0 15.6 kHz +#define SX126X_LORA_BW_20_8 0x09 // 7 0 20.8 kHz +#define SX126X_LORA_BW_31_25 0x02 // 7 0 31.25 kHz +#define SX126X_LORA_BW_41_7 0x0A // 7 0 41.7 kHz +#define SX126X_LORA_BW_62_5 0x03 // 7 0 62.5 kHz +#define SX126X_LORA_BW_125_0 0x04 // 7 0 125.0 kHz +#define SX126X_LORA_BW_250_0 0x05 // 7 0 250.0 kHz +#define SX126X_LORA_BW_500_0 0x06 // 7 0 500.0 kHz +#define SX126X_LORA_CR_4_5 0x01 // 7 0 LoRa coding rate: 4/5 +#define SX126X_LORA_CR_4_6 0x02 // 7 0 4/6 +#define SX126X_LORA_CR_4_7 0x03 // 7 0 4/7 +#define SX126X_LORA_CR_4_8 0x04 // 7 0 4/8 +#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF 0x00 // 7 0 LoRa low data rate optimization: disabled +#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON 0x01 // 7 0 enabled + +//SX126X_CMD_SET_PACKET_PARAMS +#define SX126X_GFSK_PREAMBLE_DETECT_OFF 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled +#define SX126X_GFSK_PREAMBLE_DETECT_8 0x04 // 7 0 8 bits +#define SX126X_GFSK_PREAMBLE_DETECT_16 0x05 // 7 0 16 bits +#define SX126X_GFSK_PREAMBLE_DETECT_24 0x06 // 7 0 24 bits +#define SX126X_GFSK_PREAMBLE_DETECT_32 0x07 // 7 0 32 bits +#define SX126X_GFSK_ADDRESS_FILT_OFF 0x00 // 7 0 GFSK address filtering: disabled +#define SX126X_GFSK_ADDRESS_FILT_NODE 0x01 // 7 0 node only +#define SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST 0x02 // 7 0 node and broadcast +#define SX126X_GFSK_PACKET_FIXED 0x00 // 7 0 GFSK packet type: fixed (payload length known in advance to both sides) +#define SX126X_GFSK_PACKET_VARIABLE 0x01 // 7 0 variable (payload length added to packet) +#define SX126X_GFSK_CRC_OFF 0x01 // 7 0 GFSK packet CRC: disabled +#define SX126X_GFSK_CRC_1_BYTE 0x00 // 7 0 1 byte +#define SX126X_GFSK_CRC_2_BYTE 0x02 // 7 0 2 byte +#define SX126X_GFSK_CRC_1_BYTE_INV 0x04 // 7 0 1 byte, inverted +#define SX126X_GFSK_CRC_2_BYTE_INV 0x06 // 7 0 2 byte, inverted +#define SX126X_GFSK_WHITENING_OFF 0x00 // 7 0 GFSK data whitening: disabled +#define SX126X_GFSK_WHITENING_ON 0x01 // 7 0 enabled +#define SX126X_LORA_HEADER_EXPLICIT 0x00 // 7 0 LoRa header mode: explicit +#define SX126X_LORA_HEADER_IMPLICIT 0x01 // 7 0 implicit +#define SX126X_LORA_CRC_OFF 0x00 // 7 0 LoRa CRC mode: disabled +#define SX126X_LORA_CRC_ON 0x01 // 7 0 enabled +#define SX126X_LORA_IQ_STANDARD 0x00 // 7 0 LoRa IQ setup: standard +#define SX126X_LORA_IQ_INVERTED 0x01 // 7 0 inverted + +//SX126X_CMD_SET_CAD_PARAMS +#define SX126X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1 +#define SX126X_CAD_ON_2_SYMB 0x01 // 7 0 2 +#define SX126X_CAD_ON_4_SYMB 0x02 // 7 0 4 +#define SX126X_CAD_ON_8_SYMB 0x03 // 7 0 8 +#define SX126X_CAD_ON_16_SYMB 0x04 // 7 0 16 +#define SX126X_CAD_GOTO_STDBY 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode +#define SX126X_CAD_GOTO_RX 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected + +//SX126X_CMD_GET_STATUS +#define SX126X_STATUS_MODE_STDBY_RC 0b00100000 // 6 4 current chip mode: STDBY_RC +#define SX126X_STATUS_MODE_STDBY_XOSC 0b00110000 // 6 4 STDBY_XOSC +#define SX126X_STATUS_MODE_FS 0b01000000 // 6 4 FS +#define SX126X_STATUS_MODE_RX 0b01010000 // 6 4 RX +#define SX126X_STATUS_MODE_TX 0b01100000 // 6 4 TX +#define SX126X_STATUS_DATA_AVAILABLE 0b00000100 // 3 1 command status: packet received and data can be retrieved +#define SX126X_STATUS_CMD_TIMEOUT 0b00000110 // 3 1 SPI command timed out +#define SX126X_STATUS_CMD_INVALID 0b00001000 // 3 1 invalid SPI command +#define SX126X_STATUS_CMD_FAILED 0b00001010 // 3 1 SPI command failed to execute +#define SX126X_STATUS_TX_DONE 0b00001100 // 3 1 packet transmission done +#define SX126X_STATUS_SPI_FAILED 0b11111111 // 7 0 SPI transaction failed + +//SX126X_CMD_GET_PACKET_STATUS +#define SX126X_GFSK_RX_STATUS_PREAMBLE_ERR 0b10000000 // 7 7 GFSK Rx status: preamble error +#define SX126X_GFSK_RX_STATUS_SYNC_ERR 0b01000000 // 6 6 sync word error +#define SX126X_GFSK_RX_STATUS_ADRS_ERR 0b00100000 // 5 5 address error +#define SX126X_GFSK_RX_STATUS_CRC_ERR 0b00010000 // 4 4 CRC error +#define SX126X_GFSK_RX_STATUS_LENGTH_ERR 0b00001000 // 3 3 length error +#define SX126X_GFSK_RX_STATUS_ABORT_ERR 0b00000100 // 2 2 abort error +#define SX126X_GFSK_RX_STATUS_PACKET_RECEIVED 0b00000010 // 2 2 packet received +#define SX126X_GFSK_RX_STATUS_PACKET_SENT 0b00000001 // 2 2 packet sent + +//SX126X_CMD_GET_DEVICE_ERRORS +#define SX126X_PA_RAMP_ERR 0b100000000 // 8 8 device errors: PA ramping failed +#define SX126X_PLL_LOCK_ERR 0b001000000 // 6 6 PLL failed to lock +#define SX126X_XOSC_START_ERR 0b000100000 // 5 5 crystal oscillator failed to start +#define SX126X_IMG_CALIB_ERR 0b000010000 // 4 4 image calibration failed +#define SX126X_ADC_CALIB_ERR 0b000001000 // 3 3 ADC calibration failed +#define SX126X_PLL_CALIB_ERR 0b000000100 // 2 2 PLL calibration failed +#define SX126X_RC13M_CALIB_ERR 0b000000010 // 1 1 RC13M calibration failed +#define SX126X_RC64K_CALIB_ERR 0b000000001 // 0 0 RC64K calibration failed + + +// SX126X SPI register variables +//SX126X_REG_LORA_SYNC_WORD_MSB + LSB +//#define SX126X_SYNC_WORD_PUBLIC 0x3444 +#define SX126X_SYNC_WORD_PUBLIC 0x24B4 //meshtastic +#define SX126X_SYNC_WORD_PRIVATE 0x1424 + +#define SX126x_TXMODE_ASYNC 0x01 +#define SX126x_TXMODE_SYNC 0x02 +#define SX126x_TXMODE_BACK2RX 0x04 + +// Public function +void LoRaInit(void); +int16_t LoRaBegin(uint32_t frequencyInHz, int8_t txPowerInDbm, float tcxoVoltage, bool useRegulatorLDO); +void LoRaConfig(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, bool crcOn, bool invertIrq); +uint8_t LoRaReceive(uint8_t *pData, int16_t len); +bool LoRaSend(uint8_t *pData, int16_t len, uint8_t mode); +void LoRaDebugPrint(bool enable); + +// Private function +void spi_write_byte(uint8_t* Dataout, size_t DataLength ); +void spi_read_byte(uint8_t* Datain, uint8_t* Dataout, size_t DataLength ); +uint8_t spi_transfer(uint8_t address); + +bool ReceiveMode(void); +void GetPacketStatus(int8_t *rssiPacket, int8_t *snrPacket); +void SetTxPower(int8_t txPowerInDbm); + +void FixInvertedIQ(uint8_t iqConfig); +void SetDio3AsTcxoCtrl(float voltage, uint32_t delay); +void SetDio2AsRfSwitchCtrl(uint8_t enable); +void Reset(void); +void SetStandby(uint8_t mode); +void SetRfFrequency(uint32_t frequency); +void Calibrate(uint8_t calibParam); +void CalibrateImage(uint32_t frequency); +void SetRegulatorMode(uint8_t mode); +void SetBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress); +void SetPowerConfig(int8_t power, uint8_t rampTime); +void SetOvercurrentProtection(float currentLimit); +void SetSyncWord(int16_t sync); +void SetPaConfig(uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut); +void SetDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask); +void SetStopRxTimerOnPreambleDetect(bool enable); +void SetLoRaSymbNumTimeout(uint8_t SymbNum); +void SetPacketType(uint8_t packetType); +void SetModulationParams(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint8_t lowDataRateOptimize); +void SetCadParams(uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout); +void SetCad(); +uint8_t GetStatus(void); +uint16_t GetIrqStatus(void); +void ClearIrqStatus(uint16_t irq); +void SetRx(uint32_t timeout); +void SetTx(uint32_t timeoutInMs); +int GetPacketLost(); +uint8_t GetRssiInst(); +void GetRxBufferStatus(uint8_t *payloadLength, uint8_t *rxStartBufferPointer); +void Wakeup(void); +void WaitForIdleBegin(unsigned long timeout, char *text); +bool WaitForIdle(unsigned long timeout, char *text, bool stop); +uint8_t ReadBuffer(uint8_t *rxData, int16_t rxDataLen); +void WriteBuffer(uint8_t *txData, int16_t txDataLen); +void WriteRegister(uint16_t reg, uint8_t* data, uint8_t numBytes); +void ReadRegister(uint16_t reg, uint8_t* data, uint8_t numBytes); +void WriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes); +uint8_t WriteCommand2(uint8_t cmd, uint8_t* data, uint8_t numBytes); +void ReadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes); +void SPItransfer(uint8_t cmd, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy); +void LoRaError(int error); + + +#endif