#include "lora_transport.h" #include #include #include static uint16_t crc16_ccitt(const uint8_t *data, size_t len) { uint16_t crc = 0xFFFF; for (size_t i = 0; i < len; ++i) { crc ^= static_cast(data[i]) << 8; for (uint8_t b = 0; b < 8; ++b) { if (crc & 0x8000) { crc = (crc << 1) ^ 0x1021; } else { crc <<= 1; } } } return crc; } void lora_init() { SPI.begin(PIN_LORA_SCK, PIN_LORA_MISO, PIN_LORA_MOSI, PIN_LORA_NSS); LoRa.setPins(PIN_LORA_NSS, PIN_LORA_RST, PIN_LORA_DIO0); LoRa.begin(LORA_FREQUENCY); LoRa.setSpreadingFactor(LORA_SPREADING_FACTOR); LoRa.setSignalBandwidth(LORA_BANDWIDTH); LoRa.setCodingRate4(LORA_CODING_RATE); LoRa.enableCrc(); LoRa.setSyncWord(LORA_SYNC_WORD); } bool lora_send(const LoraPacket &pkt) { if (LORA_SEND_BYPASS) { return true; } uint8_t buffer[5 + LORA_MAX_PAYLOAD + 2]; size_t idx = 0; buffer[idx++] = pkt.protocol_version; buffer[idx++] = static_cast(pkt.role); buffer[idx++] = static_cast(pkt.device_id_short >> 8); buffer[idx++] = static_cast(pkt.device_id_short & 0xFF); buffer[idx++] = static_cast(pkt.payload_type); if (pkt.payload_len > LORA_MAX_PAYLOAD) { return false; } memcpy(&buffer[idx], pkt.payload, pkt.payload_len); idx += pkt.payload_len; uint16_t crc = crc16_ccitt(buffer, idx); buffer[idx++] = static_cast(crc >> 8); buffer[idx++] = static_cast(crc & 0xFF); LoRa.beginPacket(); LoRa.write(buffer, idx); int result = LoRa.endPacket(); return result == 1; } bool lora_receive(LoraPacket &pkt, uint32_t timeout_ms) { uint32_t start = millis(); while (true) { int packet_size = LoRa.parsePacket(); if (packet_size > 0) { if (packet_size < 7) { while (LoRa.available()) { LoRa.read(); } return false; } uint8_t buffer[5 + LORA_MAX_PAYLOAD + 2]; size_t len = 0; while (LoRa.available() && len < sizeof(buffer)) { buffer[len++] = LoRa.read(); } if (len < 7) { return false; } uint16_t crc_calc = crc16_ccitt(buffer, len - 2); uint16_t crc_rx = static_cast(buffer[len - 2] << 8) | buffer[len - 1]; if (crc_calc != crc_rx) { return false; } if (buffer[0] != PROTOCOL_VERSION) { return false; } pkt.protocol_version = buffer[0]; pkt.role = static_cast(buffer[1]); pkt.device_id_short = static_cast(buffer[2] << 8) | buffer[3]; pkt.payload_type = static_cast(buffer[4]); pkt.payload_len = len - 7; if (pkt.payload_len > LORA_MAX_PAYLOAD) { return false; } memcpy(pkt.payload, &buffer[5], pkt.payload_len); pkt.rssi_dbm = static_cast(LoRa.packetRssi()); pkt.snr_db = LoRa.packetSnr(); return true; } if (timeout_ms == 0 || millis() - start >= timeout_ms) { return false; } delay(5); } } void lora_idle() { LoRa.idle(); } void lora_sleep() { LoRa.sleep(); } void lora_receive_continuous() { LoRa.receive(); } bool lora_receive_window(LoraPacket &pkt, uint32_t timeout_ms) { if (timeout_ms == 0) { return false; } LoRa.receive(); bool got = lora_receive(pkt, timeout_ms); LoRa.sleep(); return got; } uint32_t lora_airtime_ms(size_t packet_len) { if (packet_len == 0) { return 0; } const double bw = static_cast(LORA_BANDWIDTH); const double sf = static_cast(LORA_SPREADING_FACTOR); const double cr = static_cast(LORA_CODING_RATE - 4); // coding rate denominator: 4/(4+cr) const double tsym = (1 << LORA_SPREADING_FACTOR) / bw; const double t_preamble = (static_cast(LORA_PREAMBLE_LEN) + 4.25) * tsym; const bool low_data_rate_opt = (LORA_SPREADING_FACTOR >= 11) && (LORA_BANDWIDTH <= 125000); const double de = low_data_rate_opt ? 1.0 : 0.0; const double ih = 0.0; const double crc = 1.0; const double payload_symb_nb = 8.0 + max( ceil((8.0 * packet_len - 4.0 * sf + 28.0 + 16.0 * crc - 20.0 * ih) / (4.0 * (sf - 2.0 * de))) * (cr + 4.0), 0.0); const double t_payload = payload_symb_nb * tsym; const double t_packet = t_preamble + t_payload; return static_cast(ceil(t_packet * 1000.0)); }