Add SX1280 driver and split driver in three parts
This commit is contained in:
		
							parent
							
								
									252b11ab90
								
							
						
					
					
						commit
						78a02c5ae4
					
				
							
								
								
									
										17
									
								
								Config.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Config.h
									
									
									
									
									
								
							| @ -315,8 +315,9 @@ | ||||
|             #define HAS_NP false | ||||
|             #define HAS_SD false | ||||
|             #define HAS_TCXO true | ||||
|             #define HAS_RXEN_BUSY true | ||||
|             #define MODEM SX1262  | ||||
|             #define HAS_RF_SWITCH_RX_TX true | ||||
|             #define HAS_BUSY true | ||||
|             #define MODEM SX1280 | ||||
|              | ||||
|             #define CONFIG_UART_BUFFER_SIZE 6144 | ||||
|             #define CONFIG_QUEUE_SIZE 6144 | ||||
| @ -325,14 +326,14 @@ | ||||
|             #define EEPROM_OFFSET EEPROM_SIZE+0xED000-EEPROM_RESERVED | ||||
| 
 | ||||
|             // following pins are for the sx1262
 | ||||
|             const int pin_rxen = 37; | ||||
|             /*const int pin_rxen = 37;
 | ||||
|             const int pin_reset = 38; | ||||
|             const int pin_cs = 42; | ||||
|             const int pin_sclk = 43; | ||||
|             const int pin_mosi = 44; | ||||
|             const int pin_miso = 45; | ||||
|             const int pin_busy = 46; | ||||
|             const int pin_dio = 47; | ||||
|             const int pin_dio = 47;*/ | ||||
|             const int pin_led_rx = LED_BLUE; | ||||
|             const int pin_led_tx = LED_GREEN; | ||||
|             const int pin_tcxo_enable = -1; | ||||
| @ -351,12 +352,16 @@ | ||||
| 
 | ||||
| 	#define eeprom_addr(a) (a+EEPROM_OFFSET) | ||||
| 
 | ||||
|     #ifndef HAS_RXEN_BUSY | ||||
|     #ifndef HAS_RF_SWITCH_RX_TX | ||||
|         const int pin_rxen = -1; | ||||
|         const int pin_txen = -1; | ||||
|     #endif | ||||
| 
 | ||||
|     #ifndef HAS_BUSY | ||||
|         const int pin_busy = -1; | ||||
|     #endif | ||||
| 
 | ||||
|     #if MODEM == SX1262 && defined(NRF52840_XXAA) | ||||
|     #if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA) | ||||
|         SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi); | ||||
|     #endif | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								Modem.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Modem.h
									
									
									
									
									
								
							| @ -1,3 +1,4 @@ | ||||
| #define SX1276 0x01 | ||||
| #define SX1278 0x02 | ||||
| #define SX1262 0x03 | ||||
| #define SX1280 0x04 | ||||
|  | ||||
| @ -93,16 +93,22 @@ void setup() { | ||||
| 
 | ||||
|   // Set chip select, reset and interrupt
 | ||||
|   // pins for the LoRa module
 | ||||
|   LoRa.setPins(pin_cs, pin_reset, pin_dio, pin_rxen, pin_busy); | ||||
|   #if MODEM == SX1276 || MODEM == SX1278 | ||||
|   LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy); | ||||
|   #elif MODEM == SX1262 | ||||
|   LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen); | ||||
|   #elif MODEM == SX1280 | ||||
|   LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen, pin_txen); | ||||
|   #endif | ||||
|    | ||||
|   #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52 | ||||
|     init_channel_stats(); | ||||
| 
 | ||||
|     // Check installed transceiver chip and
 | ||||
|     // probe boot parameters.
 | ||||
|     if (LoRa.preInit()) { | ||||
|     if (LoRa->preInit()) { | ||||
|       modem_installed = true; | ||||
|       uint32_t lfr = LoRa.getFrequency(); | ||||
|       uint32_t lfr = LoRa->getFrequency(); | ||||
|       if (lfr == 0) { | ||||
|         // Normal boot
 | ||||
|       } else if (lfr == M_FRQ_R) { | ||||
| @ -115,7 +121,7 @@ void setup() { | ||||
|       } else { | ||||
|         // Unknown boot
 | ||||
|       } | ||||
|       LoRa.setFrequency(M_FRQ_S); | ||||
|       LoRa->setFrequency(M_FRQ_S); | ||||
|     } else { | ||||
|       modem_installed = false; | ||||
|     } | ||||
| @ -158,14 +164,14 @@ void setup() { | ||||
|   // Validate board health, EEPROM and config
 | ||||
|   validate_status(); | ||||
| 
 | ||||
|   if (op_mode != MODE_TNC) LoRa.setFrequency(0); | ||||
|   if (op_mode != MODE_TNC) LoRa->setFrequency(0); | ||||
| } | ||||
| 
 | ||||
| void lora_receive() { | ||||
|   if (!implicit) { | ||||
|     LoRa.receive(); | ||||
|     LoRa->receive(); | ||||
|   } else { | ||||
|     LoRa.receive(implicit_l); | ||||
|     LoRa->receive(implicit_l); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -187,7 +193,7 @@ inline void kiss_write_packet() { | ||||
| 
 | ||||
| inline void getPacketData(uint16_t len) { | ||||
|   while (len-- && read_len < MTU) { | ||||
|     pbuf[read_len++] = LoRa.read(); | ||||
|     pbuf[read_len++] = LoRa->read(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -198,7 +204,7 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|     // by combining two raw LoRa packets.
 | ||||
|     // We read the 1-byte header and extract
 | ||||
|     // packet sequence number and split flags
 | ||||
|     uint8_t header   = LoRa.read(); packet_size--; | ||||
|     uint8_t header   = LoRa->read(); packet_size--; | ||||
|     uint8_t sequence = packetSequence(header); | ||||
|     bool    ready    = false; | ||||
| 
 | ||||
| @ -210,8 +216,8 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|       seq = sequence; | ||||
| 
 | ||||
|       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 | ||||
|         last_rssi = LoRa.packetRssi(); | ||||
|         last_snr_raw = LoRa.packetSnrRaw(); | ||||
|         last_rssi = LoRa->packetRssi(); | ||||
|         last_snr_raw = LoRa->packetSnrRaw(); | ||||
|       #endif | ||||
| 
 | ||||
|       getPacketData(packet_size); | ||||
| @ -223,8 +229,8 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|        | ||||
| 
 | ||||
|       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 | ||||
|         last_rssi = (last_rssi+LoRa.packetRssi())/2; | ||||
|         last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2; | ||||
|         last_rssi = (last_rssi+LoRa->packetRssi())/2; | ||||
|         last_snr_raw = (last_snr_raw+LoRa->packetSnrRaw())/2; | ||||
|       #endif | ||||
| 
 | ||||
|       getPacketData(packet_size); | ||||
| @ -241,8 +247,8 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|       seq = sequence; | ||||
| 
 | ||||
|       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 | ||||
|         last_rssi = LoRa.packetRssi(); | ||||
|         last_snr_raw = LoRa.packetSnrRaw(); | ||||
|         last_rssi = LoRa->packetRssi(); | ||||
|         last_snr_raw = LoRa->packetSnrRaw(); | ||||
|       #endif | ||||
| 
 | ||||
|       getPacketData(packet_size); | ||||
| @ -260,8 +266,8 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|       } | ||||
| 
 | ||||
|       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 | ||||
|         last_rssi = LoRa.packetRssi(); | ||||
|         last_snr_raw = LoRa.packetSnrRaw(); | ||||
|         last_rssi = LoRa->packetRssi(); | ||||
|         last_snr_raw = LoRa->packetSnrRaw(); | ||||
|       #endif | ||||
| 
 | ||||
|       getPacketData(packet_size); | ||||
| @ -287,8 +293,8 @@ void ISR_VECT receive_callback(int packet_size) { | ||||
|     read_len = 0; | ||||
| 
 | ||||
|     #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52 | ||||
|       last_rssi = LoRa.packetRssi(); | ||||
|       last_snr_raw = LoRa.packetSnrRaw(); | ||||
|       last_rssi = LoRa->packetRssi(); | ||||
|       last_snr_raw = LoRa->packetSnrRaw(); | ||||
|       getPacketData(packet_size); | ||||
| 
 | ||||
|       // We first signal the RSSI of the
 | ||||
| @ -310,7 +316,7 @@ bool startRadio() { | ||||
|   update_radio_lock(); | ||||
|   if (!radio_online && !console_active) { | ||||
|     if (!radio_locked && hw_ready) { | ||||
|       if (!LoRa.begin(lora_freq)) { | ||||
|       if (!LoRa->begin(lora_freq)) { | ||||
|         // The radio could not be started.
 | ||||
|         // Indicate this failure over both the
 | ||||
|         // serial port and with the onboard LEDs
 | ||||
| @ -329,9 +335,9 @@ bool startRadio() { | ||||
|         setCodingRate(); | ||||
|         getFrequency(); | ||||
| 
 | ||||
|         LoRa.enableCrc(); | ||||
|         LoRa->enableCrc(); | ||||
| 
 | ||||
|         LoRa.onReceive(receive_callback); | ||||
|         LoRa->onReceive(receive_callback); | ||||
| 
 | ||||
|         lora_receive(); | ||||
| 
 | ||||
| @ -360,7 +366,7 @@ bool startRadio() { | ||||
| } | ||||
| 
 | ||||
| void stopRadio() { | ||||
|   LoRa.end(); | ||||
|   LoRa->end(); | ||||
|   radio_online = false; | ||||
| } | ||||
| 
 | ||||
| @ -469,23 +475,23 @@ void transmit(uint16_t size) { | ||||
|         header = header | FLAG_SPLIT; | ||||
|       } | ||||
| 
 | ||||
|       LoRa.beginPacket(); | ||||
|       LoRa.write(header); written++; | ||||
|       LoRa->beginPacket(); | ||||
|       LoRa->write(header); written++; | ||||
| 
 | ||||
|       for (uint16_t i=0; i < size; i++) { | ||||
|         LoRa.write(tbuf[i]); | ||||
|         LoRa->write(tbuf[i]); | ||||
| 
 | ||||
|         written++; | ||||
| 
 | ||||
|         if (written == 255) { | ||||
|           LoRa.endPacket(); add_airtime(written); | ||||
|           LoRa.beginPacket(); | ||||
|           LoRa.write(header); | ||||
|           LoRa->endPacket(); add_airtime(written); | ||||
|           LoRa->beginPacket(); | ||||
|           LoRa->write(header); | ||||
|           written = 1; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       LoRa.endPacket(); add_airtime(written); | ||||
|       LoRa->endPacket(); add_airtime(written); | ||||
|     } else { | ||||
|       // In promiscuous mode, we only send out
 | ||||
|       // plain raw LoRa packets with a maximum
 | ||||
| @ -501,17 +507,17 @@ void transmit(uint16_t size) { | ||||
|       // If implicit header mode has been set,
 | ||||
|       // set packet length to payload data length
 | ||||
|       if (!implicit) { | ||||
|         LoRa.beginPacket(); | ||||
|         LoRa->beginPacket(); | ||||
|       } else { | ||||
|         LoRa.beginPacket(size); | ||||
|         LoRa->beginPacket(size); | ||||
|       } | ||||
| 
 | ||||
|       for (uint16_t i=0; i < size; i++) { | ||||
|         LoRa.write(tbuf[i]); | ||||
|         LoRa->write(tbuf[i]); | ||||
| 
 | ||||
|         written++; | ||||
|       } | ||||
|       LoRa.endPacket(); add_airtime(written); | ||||
|       LoRa->endPacket(); add_airtime(written); | ||||
|     } | ||||
|   } else { | ||||
|     kiss_indicate_error(ERROR_TXFAILED); | ||||
| @ -631,7 +637,7 @@ void serialCallback(uint8_t sbyte) { | ||||
|         kiss_indicate_spreadingfactor(); | ||||
|       } else { | ||||
|         int sf = sbyte; | ||||
|         if (sf < 6) sf = 6; | ||||
|         if (sf < 5) sf = 5; | ||||
|         if (sf > 12) sf = 12; | ||||
| 
 | ||||
|         lora_sf = sf; | ||||
| @ -938,8 +944,8 @@ void updateModemStatus() { | ||||
|     portENTER_CRITICAL(); | ||||
|   #endif | ||||
| 
 | ||||
|   uint8_t status = LoRa.modemStatus(); | ||||
|   current_rssi = LoRa.currentRssi(); | ||||
|   uint8_t status = LoRa->modemStatus(); | ||||
|   current_rssi = LoRa->currentRssi(); | ||||
|   last_status_update = millis(); | ||||
| 
 | ||||
|   #if MCU_VARIANT == MCU_ESP32 | ||||
| @ -1158,8 +1164,8 @@ void loop() { | ||||
|     #if MCU_VARIANT == MCU_ESP32 | ||||
|       if (packet_ready) { | ||||
|         portENTER_CRITICAL(&update_lock); | ||||
|         last_rssi = LoRa.packetRssi(); | ||||
|         last_snr_raw = LoRa.packetSnrRaw(); | ||||
|         last_rssi = LoRa->packetRssi(); | ||||
|         last_snr_raw = LoRa->packetSnrRaw(); | ||||
|         portEXIT_CRITICAL(&update_lock); | ||||
|         kiss_indicate_stat_rssi(); | ||||
|         kiss_indicate_stat_snr(); | ||||
| @ -1173,8 +1179,8 @@ void loop() { | ||||
|     #elif MCU_VARIANT == MCU_NRF52 | ||||
|       if (packet_ready) { | ||||
|         portENTER_CRITICAL(); | ||||
|         last_rssi = LoRa.packetRssi(); | ||||
|         last_snr_raw = LoRa.packetSnrRaw(); | ||||
|         last_rssi = LoRa->packetRssi(); | ||||
|         last_snr_raw = LoRa->packetSnrRaw(); | ||||
|         portEXIT_CRITICAL(); | ||||
|         kiss_indicate_stat_rssi(); | ||||
|         kiss_indicate_stat_snr(); | ||||
| @ -1199,7 +1205,7 @@ void loop() { | ||||
|               } | ||||
| 
 | ||||
|               if (!dcd) { | ||||
|                 uint8_t csma_r = (uint8_t)random(256); | ||||
|                 uint8_t csma_r = (uint8_t)random(20); // updated to increase bitrate: todo check
 | ||||
|                 if (csma_p >= csma_r) { | ||||
|                   flushQueue(); | ||||
|                 } else { | ||||
|  | ||||
							
								
								
									
										69
									
								
								Utilities.h
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								Utilities.h
									
									
									
									
									
								
							| @ -22,7 +22,18 @@ | ||||
|     int written_bytes = 0; | ||||
| #endif | ||||
| #include <stddef.h> | ||||
| #include "LoRa.h" | ||||
| 
 | ||||
| #if MODEM == SX1262 | ||||
| #include "sx126x.h" | ||||
| sx126x *LoRa = &sx126x_modem; | ||||
| #elif MODEM == SX1276 || MODEM == SX1278 | ||||
| #include "sx127x.h" | ||||
| sx127x *LoRa = &sx127x_modem; | ||||
| #elif MODEM == SX1280 | ||||
| #include "sx128x.h" | ||||
| sx128x *LoRa = &sx128x_modem; | ||||
| #endif | ||||
| 
 | ||||
| #include "ROM.h" | ||||
| #include "Framing.h" | ||||
| #include "MD5.h" | ||||
| @ -657,11 +668,7 @@ void kiss_indicate_stat_tx() { | ||||
| } | ||||
| 
 | ||||
| void kiss_indicate_stat_rssi() { | ||||
|     #if MODEM == SX1276 || MODEM == SX1278 | ||||
|         uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); | ||||
|     #elif MODEM == SX1262 | ||||
|         int8_t packet_rssi_val = (int8_t)(last_rssi+rssi_offset); | ||||
|     #endif | ||||
|     uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset); | ||||
| 	serial_write(FEND); | ||||
| 	serial_write(CMD_STAT_RSSI); | ||||
| 	escaped_serial_write(packet_rssi_val); | ||||
| @ -975,7 +982,7 @@ inline uint8_t packetSequence(uint8_t header) { | ||||
| } | ||||
| 
 | ||||
| void setPreamble() { | ||||
| 	if (radio_online) LoRa.setPreambleLength(lora_preamble_symbols); | ||||
| 	if (radio_online) LoRa->setPreambleLength(lora_preamble_symbols); | ||||
| 	kiss_indicate_phy_stats(); | ||||
| } | ||||
| 
 | ||||
| @ -1002,12 +1009,12 @@ void updateBitrate() { | ||||
| } | ||||
| 
 | ||||
| void setSpreadingFactor() { | ||||
| 	if (radio_online) LoRa.setSpreadingFactor(lora_sf); | ||||
| 	if (radio_online) LoRa->setSpreadingFactor(lora_sf); | ||||
| 	updateBitrate(); | ||||
| } | ||||
| 
 | ||||
| void setCodingRate() { | ||||
| 	if (radio_online) LoRa.setCodingRate4(lora_cr); | ||||
| 	if (radio_online) LoRa->setCodingRate4(lora_cr); | ||||
| 	updateBitrate(); | ||||
| } | ||||
| 
 | ||||
| @ -1021,66 +1028,66 @@ void set_implicit_length(uint8_t len) { | ||||
| } | ||||
| 
 | ||||
| int getTxPower() { | ||||
| 	uint8_t txp = LoRa.getTxPower(); | ||||
| 	uint8_t txp = LoRa->getTxPower(); | ||||
| 	return (int)txp; | ||||
| } | ||||
| 
 | ||||
| void setTXPower() { | ||||
| 	if (radio_online) { | ||||
| 		if (model == MODEL_A2) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A3) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 		if (model == MODEL_A4) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 		if (model == MODEL_A7) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A2) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A3) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 		if (model == MODEL_A4) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 		if (model == MODEL_A7) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_A9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 
 | ||||
| 		if (model == MODEL_B3) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_B9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 
 | ||||
| 		if (model == MODEL_C4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_C9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 
 | ||||
| 		if (model == MODEL_E4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_E9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 
 | ||||
| 		if (model == MODEL_FE) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_FF) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 		if (model == MODEL_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN); | ||||
| 		if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void getBandwidth() { | ||||
| 	if (radio_online) { | ||||
| 			lora_bw = LoRa.getSignalBandwidth(); | ||||
| 			lora_bw = LoRa->getSignalBandwidth(); | ||||
| 	} | ||||
| 	updateBitrate(); | ||||
| } | ||||
| 
 | ||||
| void setBandwidth() { | ||||
| 	if (radio_online) { | ||||
| 		LoRa.setSignalBandwidth(lora_bw); | ||||
| 		LoRa->setSignalBandwidth(lora_bw); | ||||
| 		getBandwidth(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void getFrequency() { | ||||
| 	if (radio_online) { | ||||
| 		lora_freq = LoRa.getFrequency(); | ||||
| 		lora_freq = LoRa->getFrequency(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void setFrequency() { | ||||
| 	if (radio_online) { | ||||
| 		LoRa.setFrequency(lora_freq); | ||||
| 		LoRa->setFrequency(lora_freq); | ||||
| 		getFrequency(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| uint8_t getRandom() { | ||||
| 	if (radio_online) { | ||||
| 		return LoRa.random(); | ||||
| 		return LoRa->random(); | ||||
| 	} else { | ||||
| 		return 0x00; | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										911
									
								
								sx126x.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										911
									
								
								sx126x.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,911 @@ | ||||
| // Copyright (c) Sandeep Mistry. All rights reserved.
 | ||||
| // Licensed under the MIT license.
 | ||||
| 
 | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #include "sx126x.h" | ||||
| 
 | ||||
| #define MCU_1284P 0x91 | ||||
| #define MCU_2560  0x92 | ||||
| #define MCU_ESP32 0x81 | ||||
| #define MCU_NRF52 0x71 | ||||
| #if defined(__AVR_ATmega1284P__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_1284P | ||||
| #elif defined(__AVR_ATmega2560__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_2560 | ||||
| #elif defined(ESP32) | ||||
|   #define PLATFORM PLATFORM_ESP32 | ||||
|   #define MCU_VARIANT MCU_ESP32 | ||||
| #elif defined(NRF52840_XXAA) | ||||
|   #define PLATFORM PLATFORM_NRF52 | ||||
|   #define MCU_VARIANT MCU_NRF52 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef MCU_VARIANT | ||||
|   #error No MCU variant defined, cannot compile | ||||
| #endif | ||||
| 
 | ||||
| #if MCU_VARIANT == MCU_ESP32 | ||||
|   #include "soc/rtc_wdt.h" | ||||
|   #define ISR_VECT IRAM_ATTR | ||||
| #else | ||||
|   #define ISR_VECT | ||||
| #endif | ||||
| 
 | ||||
| #define OP_RF_FREQ_6X               0x86 | ||||
| #define OP_SLEEP_6X                 0x84 | ||||
| #define OP_STANDBY_6X               0x80 | ||||
| #define OP_TX_6X                    0x83 | ||||
| #define OP_RX_6X                    0x82 | ||||
| #define OP_PA_CONFIG_6X             0x95 | ||||
| #define OP_SET_IRQ_FLAGS_6X         0x08 // also provides info such as
 | ||||
|                                       // preamble detection, etc for
 | ||||
|                                       // knowing when it's safe to switch
 | ||||
|                                       // antenna modes
 | ||||
| #define OP_CLEAR_IRQ_STATUS_6X      0x02 | ||||
| #define OP_GET_IRQ_STATUS_6X        0x12 | ||||
| #define OP_RX_BUFFER_STATUS_6X      0x13 | ||||
| #define OP_PACKET_STATUS_6X         0x14 // get snr & rssi of last packet
 | ||||
| #define OP_CURRENT_RSSI_6X          0x15 | ||||
| #define OP_MODULATION_PARAMS_6X     0x8B // bw, sf, cr, etc.
 | ||||
| #define OP_PACKET_PARAMS_6X         0x8C // crc, preamble, payload length, etc.
 | ||||
| #define OP_STATUS_6X                0xC0 | ||||
| #define OP_TX_PARAMS_6X             0x8E // set dbm, etc
 | ||||
| #define OP_PACKET_TYPE_6X           0x8A | ||||
| #define OP_BUFFER_BASE_ADDR_6X      0x8F | ||||
| #define OP_READ_REGISTER_6X         0x1D | ||||
| #define OP_WRITE_REGISTER_6X        0x0D | ||||
| #define OP_DIO3_TCXO_CTRL_6X        0x97 | ||||
| #define OP_DIO2_RF_CTRL_6X          0x9D | ||||
| #define OP_CALIBRATE_6X             0x89 | ||||
| #define IRQ_TX_DONE_MASK_6X         0x01 | ||||
| #define IRQ_RX_DONE_MASK_6X         0x02 | ||||
| #define IRQ_HEADER_DET_MASK_6X      0x10 | ||||
| #define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40 | ||||
| 
 | ||||
| #define MODE_LONG_RANGE_MODE_6X     0x01 | ||||
| 
 | ||||
| #define OP_FIFO_WRITE_6X            0x0E | ||||
| #define OP_FIFO_READ_6X             0x1E | ||||
| #define REG_OCP_6X                0x08E7 | ||||
| #define REG_LNA_6X                0x08AC // no agc in sx1262
 | ||||
| #define REG_SYNC_WORD_MSB_6X      0x0740 | ||||
| #define REG_SYNC_WORD_LSB_6X      0x0741 | ||||
| #define REG_PAYLOAD_LENGTH_6X     0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
 | ||||
| #define REG_RANDOM_GEN_6X         0x0819 | ||||
| 
 | ||||
| #define MODE_TCXO_3_3V_6X           0x07 | ||||
| 
 | ||||
| #define IRQ_PREAMBLE_DET_MASK_6X    0x04 | ||||
| #define XTAL_FREQ_6X (double)32000000 | ||||
| #define FREQ_DIV_6X (double)pow(2.0, 25.0) | ||||
| #define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) | ||||
| extern SPIClass spiModem; | ||||
| #define SPI spiModem | ||||
| 
 | ||||
| extern SPIClass SPI; | ||||
| 
 | ||||
| #define MAX_PKT_LENGTH           255 | ||||
| 
 | ||||
| sx126x::sx126x() : | ||||
|   _spiSettings(8E6, MSBFIRST, SPI_MODE0), | ||||
|   _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), | ||||
|   _frequency(0), | ||||
|   _txp(0), | ||||
|   _sf(0x07), | ||||
|   _bw(0x04), | ||||
|   _cr(0x01), | ||||
|   _ldro(0x00), | ||||
|   _packetIndex(0), | ||||
|   _preambleLength(18), | ||||
|   _implicitHeaderMode(0), | ||||
|   _payloadLength(255), | ||||
|   _crcMode(1), | ||||
|   _fifo_tx_addr_ptr(0), | ||||
|   _fifo_rx_addr_ptr(0), | ||||
|   _packet({0}), | ||||
|   _preinit_done(false), | ||||
|   _onReceive(NULL) | ||||
| { | ||||
|   // overide Stream timeout value
 | ||||
|   setTimeout(0); | ||||
| } | ||||
| 
 | ||||
| bool sx126x::preInit() { | ||||
|   // setup pins
 | ||||
|   pinMode(_ss, OUTPUT); | ||||
|   // set SS high
 | ||||
|   digitalWrite(_ss, HIGH); | ||||
|    | ||||
|   SPI.begin(); | ||||
| 
 | ||||
|   // check version (retry for up to 2 seconds)
 | ||||
|   long start = millis(); | ||||
|   uint8_t syncmsb; | ||||
|   uint8_t synclsb; | ||||
|   while (((millis() - start) < 2000) && (millis() >= start)) { | ||||
|       syncmsb = readRegister(REG_SYNC_WORD_MSB_6X); | ||||
|       synclsb = readRegister(REG_SYNC_WORD_LSB_6X); | ||||
|       if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) { | ||||
|           break; | ||||
|       } | ||||
|       delay(100); | ||||
|   } | ||||
|   if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) { | ||||
|       return false; | ||||
|   } | ||||
| 
 | ||||
|   _preinit_done = true; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx126x::readRegister(uint16_t address) | ||||
| { | ||||
|   return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); | ||||
| } | ||||
| 
 | ||||
| void sx126x::writeRegister(uint16_t address, uint8_t value) | ||||
| { | ||||
|     singleTransfer(OP_WRITE_REGISTER_6X, address, value); | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     uint8_t response; | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
|     SPI.transfer((address & 0xFF00) >> 8); | ||||
|     SPI.transfer(address & 0x00FF); | ||||
|     if (opcode == OP_READ_REGISTER_6X) { | ||||
|         SPI.transfer(0x00); | ||||
|     } | ||||
|     response = SPI.transfer(value); | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| void sx126x::rxAntEnable() | ||||
| { | ||||
|         uint8_t byte = 0x01; | ||||
|         // enable dio2 rf switch
 | ||||
|         executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);  | ||||
|         digitalWrite(_rxen, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx126x::loraMode() { | ||||
|     // enable lora mode on the SX1262 chip
 | ||||
|     uint8_t mode = MODE_LONG_RANGE_MODE_6X; | ||||
|     executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); | ||||
| } | ||||
| 
 | ||||
| void sx126x::waitOnBusy() { | ||||
|     if (_busy != -1) { | ||||
|         while (digitalRead(_busy) == HIGH) | ||||
|         { | ||||
|             // do nothing
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         SPI.transfer(buffer[i]); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
|     SPI.transfer(0x00); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         buffer[i] = SPI.transfer(0x00); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx126x::writeBuffer(const uint8_t* buffer, size_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(OP_FIFO_WRITE_6X); | ||||
|     SPI.transfer(_fifo_tx_addr_ptr); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         SPI.transfer(buffer[i]); | ||||
|         _fifo_tx_addr_ptr++; | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx126x::readBuffer(uint8_t* buffer, size_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(OP_FIFO_READ_6X); | ||||
|     SPI.transfer(_fifo_rx_addr_ptr); | ||||
|     SPI.transfer(0x00); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         buffer[i] = SPI.transfer(0x00); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { | ||||
|   // because there is no access to these registers on the sx1262, we have
 | ||||
|   // to set all these parameters at once or not at all.
 | ||||
|   uint8_t buf[8]; | ||||
| 
 | ||||
|   buf[0] = sf; | ||||
|   buf[1] = bw; | ||||
|   buf[2] = cr;  | ||||
|   // low data rate toggle
 | ||||
|   buf[3] = ldro; | ||||
|   // unused params in LoRa mode
 | ||||
|   buf[4] = 0x00;  | ||||
|   buf[5] = 0x00; | ||||
|   buf[6] = 0x00; | ||||
|   buf[7] = 0x00; | ||||
| 
 | ||||
|   executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) { | ||||
|   // because there is no access to these registers on the sx1262, we have
 | ||||
|   // to set all these parameters at once or not at all.
 | ||||
|   uint8_t buf[9]; | ||||
| 
 | ||||
|   buf[0] = uint8_t((preamble & 0xFF00) >> 8); | ||||
|   buf[1] = uint8_t((preamble & 0x00FF)); | ||||
|   buf[2] = headermode; | ||||
|   buf[3] = length; | ||||
|   buf[4] = crc; | ||||
|   // standard IQ setting (no inversion)
 | ||||
|   buf[5] = 0x00;  | ||||
|   // unused params
 | ||||
|   buf[6] = 0x00;  | ||||
|   buf[7] = 0x00;  | ||||
|   buf[8] = 0x00;  | ||||
| 
 | ||||
|   executeOpcode(OP_PACKET_PARAMS_6X, buf, 9); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int sx126x::begin(long frequency) | ||||
| { | ||||
|   if (_reset != -1) { | ||||
|     pinMode(_reset, OUTPUT); | ||||
| 
 | ||||
|     // perform reset
 | ||||
|     digitalWrite(_reset, LOW); | ||||
|     delay(10); | ||||
|     digitalWrite(_reset, HIGH); | ||||
|     delay(10); | ||||
|   } | ||||
| 
 | ||||
|   if (_busy != -1) { | ||||
|       pinMode(_busy, INPUT); | ||||
|   } | ||||
| 
 | ||||
|   if (!_preinit_done) { | ||||
|     if (!preInit()) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|       //#if HAS_TCXO
 | ||||
|           // turn TCXO on
 | ||||
|           enableTCXO(); | ||||
|       //#endif
 | ||||
|       loraMode(); | ||||
|       idle(); | ||||
|       // cannot access registers in sleep mode on sx1262, set to idle instead
 | ||||
|       if (_rxen != -1) { | ||||
|           pinMode(_rxen, OUTPUT); | ||||
|           rxAntEnable(); | ||||
|       } | ||||
|       // calibrate RC64k, RC13M, PLL, ADC and image
 | ||||
|       uint8_t calibrate = 0x7F;  | ||||
|       executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); | ||||
| 
 | ||||
|       setFrequency(frequency); | ||||
| 
 | ||||
|       // set output power to 2 dBm
 | ||||
|       setTxPower(2); | ||||
| 
 | ||||
|       // set LNA boost
 | ||||
|       writeRegister(REG_LNA_6X, 0x96); | ||||
| 
 | ||||
|       // set base addresses
 | ||||
|       uint8_t basebuf[2] = {0}; | ||||
|       executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); | ||||
| 
 | ||||
|       setModulationParams(_sf, _bw, _cr, _ldro); | ||||
|       setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| void sx126x::end() | ||||
| { | ||||
|   // put in sleep mode
 | ||||
|   sleep(); | ||||
| 
 | ||||
|   // stop SPI
 | ||||
|   SPI.end(); | ||||
| 
 | ||||
|   _preinit_done = false; | ||||
| } | ||||
| 
 | ||||
| int sx126x::beginPacket(int implicitHeader) | ||||
| { | ||||
|   // put in standby mode
 | ||||
|   idle(); | ||||
| 
 | ||||
|   if (implicitHeader) { | ||||
|     implicitHeaderMode(); | ||||
|   } else { | ||||
|     explicitHeaderMode(); | ||||
|   } | ||||
| 
 | ||||
|       _payloadLength = 0; | ||||
|       _fifo_tx_addr_ptr = 0; | ||||
|       setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| int sx126x::endPacket() | ||||
| { | ||||
|       setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|       // put in single TX mode
 | ||||
|       uint8_t timeout[3] = {0}; | ||||
|       executeOpcode(OP_TX_6X, timeout, 3); | ||||
| 
 | ||||
|       uint8_t buf[2]; | ||||
| 
 | ||||
|       buf[0] = 0x00; | ||||
|       buf[1] = 0x00; | ||||
| 
 | ||||
|       executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); | ||||
| 
 | ||||
|       // wait for TX done
 | ||||
|       while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) { | ||||
|         buf[0] = 0x00; | ||||
|         buf[1] = 0x00; | ||||
|         executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); | ||||
|         yield(); | ||||
|       } | ||||
| 
 | ||||
|       // clear IRQ's
 | ||||
| 
 | ||||
|       uint8_t mask[2]; | ||||
|       mask[0] = 0x00; | ||||
|       mask[1] = IRQ_TX_DONE_MASK_6X; | ||||
|       executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx126x::modemStatus() { | ||||
|     // imitate the register status from the sx1276 / 78
 | ||||
|     uint8_t buf[2] = {0}; | ||||
| 
 | ||||
| 
 | ||||
|     executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); | ||||
| 
 | ||||
|     uint8_t clearbuf[2] = {0}; | ||||
| 
 | ||||
|     uint8_t byte = 0x00; | ||||
| 
 | ||||
|     if (buf[1] & IRQ_PREAMBLE_DET_MASK_6X != 0) { | ||||
|         byte = byte | 0x01 | 0x04; | ||||
|         // clear register after reading
 | ||||
|         clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;  | ||||
|     } | ||||
| 
 | ||||
|     if (buf[1] & IRQ_HEADER_DET_MASK_6X != 0) { | ||||
|         byte = byte | 0x02 | 0x04; | ||||
|         // clear register after reading
 | ||||
|         clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK_6X;  | ||||
|     } | ||||
| 
 | ||||
|     executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); | ||||
| 
 | ||||
|     return byte;  | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| uint8_t sx126x::currentRssiRaw() { | ||||
|     uint8_t byte = 0; | ||||
|     executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); | ||||
|     return byte; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx126x::currentRssi() { | ||||
|     uint8_t byte = 0; | ||||
|     executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); | ||||
|     int rssi = -(int(byte)) / 2; | ||||
|     return rssi - RSSI_OFFSET; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx126x::packetRssiRaw() { | ||||
|     uint8_t buf[3] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); | ||||
|     return buf[2]; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx126x::packetRssi() { | ||||
|     // may need more calculations here
 | ||||
|     uint8_t buf[3] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); | ||||
|     int pkt_rssi = -buf[0] / 2; | ||||
|     return pkt_rssi - RSSI_OFFSET; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx126x::packetSnrRaw() { | ||||
|     uint8_t buf[3] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); | ||||
|     return buf[1]; | ||||
| } | ||||
| 
 | ||||
| float ISR_VECT sx126x::packetSnr() { | ||||
|     uint8_t buf[3] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); | ||||
|     return float(buf[1]) * 0.25; | ||||
| } | ||||
| 
 | ||||
| long sx126x::packetFrequencyError() | ||||
| { | ||||
|     // todo: implement this, no idea how to check it on the sx1262
 | ||||
|     const float fError = 0.0; | ||||
|     return static_cast<long>(fError); | ||||
| } | ||||
| 
 | ||||
| size_t sx126x::write(uint8_t byte) | ||||
| { | ||||
|   return write(&byte, sizeof(byte)); | ||||
| } | ||||
| 
 | ||||
| size_t sx126x::write(const uint8_t *buffer, size_t size) | ||||
| { | ||||
|     if ((_payloadLength + size) > MAX_PKT_LENGTH) { | ||||
|         size = MAX_PKT_LENGTH - _payloadLength; | ||||
|     } | ||||
| 
 | ||||
|     // write data
 | ||||
|     writeBuffer(buffer, size); | ||||
|     _payloadLength = _payloadLength + size; | ||||
|     return size; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx126x::available() | ||||
| { | ||||
|     uint8_t buf[2] = {0}; | ||||
|     executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); | ||||
|     return buf[0] - _packetIndex; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx126x::read() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   // if received new packet
 | ||||
|   if (_packetIndex == 0) { | ||||
|       uint8_t rxbuf[2] = {0}; | ||||
|       executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); | ||||
|       int size = rxbuf[0]; | ||||
|       _fifo_rx_addr_ptr = rxbuf[1]; | ||||
| 
 | ||||
|       readBuffer(_packet, size); | ||||
|   } | ||||
| 
 | ||||
|   uint8_t byte = _packet[_packetIndex]; | ||||
|   _packetIndex++; | ||||
|   return byte; | ||||
| } | ||||
| 
 | ||||
| int sx126x::peek() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   // if received new packet
 | ||||
|   if (_packetIndex == 0) { | ||||
|       uint8_t rxbuf[2] = {0}; | ||||
|       executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); | ||||
|       int size = rxbuf[0]; | ||||
|       _fifo_rx_addr_ptr = rxbuf[1]; | ||||
| 
 | ||||
|       readBuffer(_packet, size); | ||||
|   } | ||||
| 
 | ||||
|   uint8_t b = _packet[_packetIndex]; | ||||
|   return b; | ||||
| } | ||||
| 
 | ||||
| void sx126x::flush() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void sx126x::onReceive(void(*callback)(int)) | ||||
| { | ||||
|   _onReceive = callback; | ||||
| 
 | ||||
|   if (callback) { | ||||
|     pinMode(_dio0, INPUT); | ||||
| 
 | ||||
|     // set preamble and header detection irqs, plus dio0 mask
 | ||||
|     uint8_t buf[8]; | ||||
| 
 | ||||
|     // set irq masks, enable all
 | ||||
|     buf[0] = 0xFF;  | ||||
|     buf[1] = 0xFF; | ||||
| 
 | ||||
|     // set dio0 masks
 | ||||
|     buf[2] = 0x00; | ||||
|     buf[3] = IRQ_RX_DONE_MASK_6X;  | ||||
| 
 | ||||
|     // set dio1 masks
 | ||||
|     buf[4] = 0x00;  | ||||
|     buf[5] = 0x00; | ||||
| 
 | ||||
|     // set dio2 masks
 | ||||
|     buf[6] = 0x00;  | ||||
|     buf[7] = 0x00; | ||||
| 
 | ||||
|     executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); | ||||
| #ifdef SPI_HAS_NOTUSINGINTERRUPT | ||||
|     SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #endif | ||||
|     attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING); | ||||
|   } else { | ||||
|     detachInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #ifdef SPI_HAS_NOTUSINGINTERRUPT | ||||
|     SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #endif | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx126x::receive(int size) | ||||
| { | ||||
|     if (size > 0) { | ||||
|         implicitHeaderMode(); | ||||
| 
 | ||||
|         // tell radio payload length
 | ||||
|         _payloadLength = size; | ||||
|         setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
|     } else { | ||||
|         explicitHeaderMode(); | ||||
|     } | ||||
| 
 | ||||
|     if (_rxen != -1) { | ||||
|         rxAntEnable(); | ||||
|     } | ||||
|     uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
 | ||||
|     executeOpcode(OP_RX_6X, mode, 3); | ||||
| } | ||||
| 
 | ||||
| void sx126x::idle() | ||||
| { | ||||
|       //#if HAS_TCXO
 | ||||
|           // STDBY_XOSC
 | ||||
|           uint8_t byte = 0x01; | ||||
|       //#else
 | ||||
|       //    // STDBY_RC
 | ||||
|       //    uint8_t byte = 0x00;
 | ||||
|       //#endif
 | ||||
|       executeOpcode(OP_STANDBY_6X, &byte, 1);  | ||||
| } | ||||
| 
 | ||||
| void sx126x::sleep() | ||||
| { | ||||
|     uint8_t byte = 0x00; | ||||
|     executeOpcode(OP_SLEEP_6X, &byte, 1); | ||||
| } | ||||
| 
 | ||||
| void sx126x::enableTCXO() { | ||||
|     // only tested for RAK4630, voltage may be different on other platforms
 | ||||
|     uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF}; | ||||
|     executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4); | ||||
| } | ||||
| 
 | ||||
| void sx126x::disableTCXO() { | ||||
|     // currently cannot disable on SX1262?
 | ||||
| } | ||||
| 
 | ||||
| void sx126x::setTxPower(int level, int outputPin) { | ||||
|     // currently no low power mode for SX1262 implemented, assuming PA boost
 | ||||
|      | ||||
|     // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
 | ||||
|     // RegTxClampConfig = @address 0x08D8
 | ||||
|     writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); | ||||
| 
 | ||||
|     uint8_t pa_buf[4]; | ||||
| 
 | ||||
|     pa_buf[0] = 0x04; | ||||
|     pa_buf[1] = 0x07; | ||||
|     pa_buf[2] = 0x00; | ||||
|     pa_buf[3] = 0x01; | ||||
| 
 | ||||
|     executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
 | ||||
| 
 | ||||
|     if (level > 22) { | ||||
|         level = 22; | ||||
|     } | ||||
|     else if (level < -9) { | ||||
|         level = -9; | ||||
|     } | ||||
| 
 | ||||
|     writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
 | ||||
| 
 | ||||
|     uint8_t tx_buf[2]; | ||||
| 
 | ||||
|     tx_buf[0] = level; | ||||
|     tx_buf[1] = 0x02; // ramping time - 40 microseconds
 | ||||
|      | ||||
|     executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2); | ||||
| 
 | ||||
|     _txp = level; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx126x::getTxPower() { | ||||
|     return _txp; | ||||
| } | ||||
| 
 | ||||
| void sx126x::setFrequency(long frequency) { | ||||
|   _frequency = frequency; | ||||
| 
 | ||||
|   uint8_t buf[4]; | ||||
| 
 | ||||
|   uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X); | ||||
| 
 | ||||
|   buf[0] = ((freq >> 24) & 0xFF); | ||||
|   buf[1] = ((freq >> 16) & 0xFF); | ||||
|   buf[2] = ((freq >> 8) & 0xFF); | ||||
|   buf[3] = (freq & 0xFF); | ||||
| 
 | ||||
|   executeOpcode(OP_RF_FREQ_6X, buf, 4); | ||||
| } | ||||
| 
 | ||||
| uint32_t sx126x::getFrequency() { | ||||
|     // we can't read the frequency on the sx1262 / 80
 | ||||
|     uint32_t frequency = _frequency; | ||||
| 
 | ||||
|     return frequency; | ||||
| } | ||||
| 
 | ||||
| void sx126x::setSpreadingFactor(int sf) | ||||
| { | ||||
|   if (sf < 5) { | ||||
|       sf = 5; | ||||
|   } else if (sf > 12) { | ||||
|     sf = 12; | ||||
|   } | ||||
| 
 | ||||
|   _sf = sf; | ||||
| 
 | ||||
|       setModulationParams(sf, _bw, _cr, _ldro); | ||||
|   handleLowDataRate(); | ||||
| } | ||||
| 
 | ||||
| long sx126x::getSignalBandwidth() | ||||
| { | ||||
|     int bw = _bw; | ||||
|     switch (bw) { | ||||
|         case 0x00: return 7.8E3; | ||||
|         case 0x01: return 15.6E3; | ||||
|         case 0x02: return 31.25E3; | ||||
|         case 0x03: return 62.5E3; | ||||
|         case 0x04: return 125E3; | ||||
|         case 0x05: return 250E3; | ||||
|         case 0x06: return 500E3; | ||||
|         case 0x08: return 10.4E3; | ||||
|         case 0x09: return 20.8E3; | ||||
|         case 0x0A: return 41.7E3; | ||||
|     } | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| void sx126x::handleLowDataRate(){ | ||||
|     _ldro = 1; | ||||
|     setModulationParams(_sf, _bw, _cr, _ldro); | ||||
| } | ||||
| 
 | ||||
| void sx126x::optimizeModemSensitivity(){ | ||||
|     // todo: check if there's anything the sx1262 can do here
 | ||||
| } | ||||
| 
 | ||||
| void sx126x::setSignalBandwidth(long sbw) | ||||
| { | ||||
|     if (sbw <= 7.8E3) { | ||||
|         _bw = 0x00; | ||||
|     } else if (sbw <= 10.4E3) { | ||||
|         _bw = 0x08; | ||||
|     } else if (sbw <= 15.6E3) { | ||||
|         _bw = 0x01; | ||||
|     } else if (sbw <= 20.8E3) { | ||||
|         _bw = 0x09; | ||||
|     } else if (sbw <= 31.25E3) { | ||||
|         _bw = 0x02; | ||||
|     } else if (sbw <= 41.7E3) { | ||||
|         _bw = 0x0A; | ||||
|     } else if (sbw <= 62.5E3) { | ||||
|         _bw = 0x03; | ||||
|     } else if (sbw <= 125E3) { | ||||
|         _bw = 0x04; | ||||
|     } else if (sbw <= 250E3) { | ||||
|         _bw = 0x05; | ||||
|     } else /*if (sbw <= 250E3)*/ { | ||||
|         _bw = 0x06; | ||||
|     } | ||||
| 
 | ||||
|     setModulationParams(_sf, _bw, _cr, _ldro); | ||||
| 
 | ||||
|     handleLowDataRate(); | ||||
|   optimizeModemSensitivity(); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setCodingRate4(int denominator) | ||||
| { | ||||
|   if (denominator < 5) { | ||||
|     denominator = 5; | ||||
|   } else if (denominator > 8) { | ||||
|     denominator = 8; | ||||
|   } | ||||
| 
 | ||||
|   int cr = denominator - 4; | ||||
| 
 | ||||
|   _cr = cr; | ||||
| 
 | ||||
|   setModulationParams(_sf, _bw, cr, _ldro); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setPreambleLength(long length) | ||||
| { | ||||
|     setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setSyncWord(int sw) | ||||
| { | ||||
|     writeRegister(REG_SYNC_WORD_MSB_6X, sw & 0xFF00); | ||||
|     writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF); | ||||
| } | ||||
| 
 | ||||
| void sx126x::enableCrc() | ||||
| { | ||||
|     _crcMode = 1; | ||||
|     setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx126x::disableCrc() | ||||
| { | ||||
|     _crcMode = 0; | ||||
|     setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| byte sx126x::random() | ||||
| { | ||||
|     return readRegister(REG_RANDOM_GEN_6X); | ||||
| } | ||||
| 
 | ||||
| void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) | ||||
| { | ||||
|   _ss = ss; | ||||
|   _reset = reset; | ||||
|   _dio0 = dio0; | ||||
|   _busy = busy; | ||||
|   _rxen = rxen; | ||||
| } | ||||
| 
 | ||||
| void sx126x::setSPIFrequency(uint32_t frequency) | ||||
| { | ||||
|   _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); | ||||
| } | ||||
| 
 | ||||
| void sx126x::dumpRegisters(Stream& out) | ||||
| { | ||||
|   for (int i = 0; i < 128; i++) { | ||||
|     out.print("0x"); | ||||
|     out.print(i, HEX); | ||||
|     out.print(": 0x"); | ||||
|     out.println(readRegister(i), HEX); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx126x::explicitHeaderMode() | ||||
| { | ||||
|   _implicitHeaderMode = 0; | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx126x::implicitHeaderMode() | ||||
| { | ||||
|   _implicitHeaderMode = 1; | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void ISR_VECT sx126x::handleDio0Rise() | ||||
| { | ||||
|     uint8_t buf[2]; | ||||
| 
 | ||||
|     buf[0] = 0x00; | ||||
|     buf[1] = 0x00; | ||||
| 
 | ||||
|     executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); | ||||
| 
 | ||||
|     executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); | ||||
| 
 | ||||
|     if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { | ||||
|         // received a packet
 | ||||
|         _packetIndex = 0; | ||||
| 
 | ||||
|         // read packet length
 | ||||
|         uint8_t rxbuf[2] = {0}; | ||||
|         executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); | ||||
|         int packetLength = rxbuf[0]; | ||||
| 
 | ||||
|         if (_onReceive) { | ||||
|             _onReceive(packetLength); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ISR_VECT sx126x::onDio0Rise() | ||||
| { | ||||
|     sx126x_modem.handleDio0Rise(); | ||||
| } | ||||
| 
 | ||||
| sx126x sx126x_modem; | ||||
| @ -4,8 +4,8 @@ | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #ifndef LORA_H | ||||
| #define LORA_H | ||||
| #ifndef SX126X_H | ||||
| #define SX126X_H | ||||
| 
 | ||||
| #include <Arduino.h> | ||||
| #include <SPI.h> | ||||
| @ -15,6 +15,7 @@ | ||||
| #define LORA_DEFAULT_RESET_PIN 9 | ||||
| #define LORA_DEFAULT_DIO0_PIN  2 | ||||
| #define LORA_DEFAULT_RXEN_PIN  -1 | ||||
| #define LORA_DEFAULT_TXEN_PIN  -1 | ||||
| #define LORA_DEFAULT_BUSY_PIN  -1 | ||||
| 
 | ||||
| #define PA_OUTPUT_RFO_PIN      0 | ||||
| @ -22,9 +23,9 @@ | ||||
| 
 | ||||
| #define RSSI_OFFSET 157 | ||||
| 
 | ||||
| class LoRaClass : public Stream { | ||||
| class sx126x : public Stream { | ||||
| public: | ||||
|   LoRaClass(); | ||||
|   sx126x(); | ||||
| 
 | ||||
|   int begin(long frequency); | ||||
|   void end(); | ||||
| @ -74,18 +75,16 @@ public: | ||||
|   void enableTCXO(); | ||||
|   void disableTCXO(); | ||||
| 
 | ||||
|   #if MODEM == SX1262 | ||||
|       void enableAntenna(); | ||||
|       void disableAntenna(); | ||||
|       void loraMode(); | ||||
|       void waitOnBusy(); | ||||
|       void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|       void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|       void writeBuffer(const uint8_t* buffer, size_t size); | ||||
|       void readBuffer(uint8_t* buffer, size_t size); | ||||
|       void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); | ||||
|       void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); | ||||
|   #endif | ||||
|   void rxAntEnable(); | ||||
|   void loraMode(); | ||||
|   void waitOnBusy(); | ||||
|   void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|   void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|   void writeBuffer(const uint8_t* buffer, size_t size); | ||||
|   void readBuffer(uint8_t* buffer, size_t size); | ||||
|   void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); | ||||
| 
 | ||||
|   void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); | ||||
| 
 | ||||
|   // deprecated
 | ||||
|   void crc() { enableCrc(); } | ||||
| @ -93,7 +92,7 @@ public: | ||||
| 
 | ||||
|   byte random(); | ||||
| 
 | ||||
|   void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int busy = LORA_DEFAULT_BUSY_PIN); | ||||
|   void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN); | ||||
|   void setSPIFrequency(uint32_t frequency); | ||||
| 
 | ||||
|   void dumpRegisters(Stream& out); | ||||
| @ -104,15 +103,9 @@ private: | ||||
| 
 | ||||
|   void handleDio0Rise(); | ||||
| 
 | ||||
|   #if MODEM == SX1276 || MODEM == SX1278 | ||||
|       uint8_t readRegister(uint8_t address); | ||||
|       void writeRegister(uint8_t address, uint8_t value); | ||||
|       uint8_t singleTransfer(uint8_t address, uint8_t value); | ||||
|   #elif MODEM == SX1262 | ||||
|       uint8_t readRegister(uint16_t address); | ||||
|       void writeRegister(uint16_t address, uint8_t value); | ||||
|       uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); | ||||
|   #endif | ||||
|   uint8_t readRegister(uint16_t address); | ||||
|   void writeRegister(uint16_t address, uint8_t value); | ||||
|   uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); | ||||
| 
 | ||||
|   static void onDio0Rise(); | ||||
| 
 | ||||
| @ -137,9 +130,13 @@ private: | ||||
|   int _implicitHeaderMode; | ||||
|   int _payloadLength; | ||||
|   int _crcMode; | ||||
|   int _fifo_tx_addr_ptr; | ||||
|   int _fifo_rx_addr_ptr; | ||||
|   uint8_t _packet[255]; | ||||
|   bool _preinit_done; | ||||
|   void (*_onReceive)(int); | ||||
| }; | ||||
| 
 | ||||
| extern LoRaClass LoRa; | ||||
| extern sx126x sx126x_modem; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										691
									
								
								sx127x.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										691
									
								
								sx127x.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,691 @@ | ||||
| // Copyright (c) Sandeep Mistry. All rights reserved.
 | ||||
| // Licensed under the MIT license.
 | ||||
| 
 | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #include "sx127x.h" | ||||
| 
 | ||||
| #define MCU_1284P 0x91 | ||||
| #define MCU_2560  0x92 | ||||
| #define MCU_ESP32 0x81 | ||||
| #define MCU_NRF52 0x71 | ||||
| #if defined(__AVR_ATmega1284P__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_1284P | ||||
| #elif defined(__AVR_ATmega2560__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_2560 | ||||
| #elif defined(ESP32) | ||||
|   #define PLATFORM PLATFORM_ESP32 | ||||
|   #define MCU_VARIANT MCU_ESP32 | ||||
| #elif defined(NRF52840_XXAA) | ||||
|   #define PLATFORM PLATFORM_NRF52 | ||||
|   #define MCU_VARIANT MCU_NRF52 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef MCU_VARIANT | ||||
|   #error No MCU variant defined, cannot compile | ||||
| #endif | ||||
| 
 | ||||
| #if MCU_VARIANT == MCU_ESP32 | ||||
|   #include "soc/rtc_wdt.h" | ||||
|   #define ISR_VECT IRAM_ATTR | ||||
| #else | ||||
|   #define ISR_VECT | ||||
| #endif | ||||
| 
 | ||||
| // Registers
 | ||||
| #define REG_FIFO_7X                 0x00 | ||||
| #define REG_OP_MODE_7X              0x01 | ||||
| #define REG_FRF_MSB_7X              0x06 | ||||
| #define REG_FRF_MID_7X              0x07 | ||||
| #define REG_FRF_LSB_7X              0x08 | ||||
| #define REG_PA_CONFIG_7X            0x09 | ||||
| #define REG_OCP_7X                  0x0b | ||||
| #define REG_LNA_7X                  0x0c | ||||
| #define REG_FIFO_ADDR_PTR_7X        0x0d | ||||
| #define REG_FIFO_TX_BASE_ADDR_7X    0x0e | ||||
| #define REG_FIFO_RX_BASE_ADDR_7X    0x0f | ||||
| #define REG_FIFO_RX_CURRENT_ADDR_7X 0x10 | ||||
| #define REG_IRQ_FLAGS_7X            0x12 | ||||
| #define REG_RX_NB_BYTES_7X          0x13 | ||||
| #define REG_MODEM_STAT_7X           0x18 | ||||
| #define REG_PKT_SNR_VALUE_7X        0x19 | ||||
| #define REG_PKT_RSSI_VALUE_7X       0x1a | ||||
| #define REG_RSSI_VALUE_7X           0x1b | ||||
| #define REG_MODEM_CONFIG_1_7X       0x1d | ||||
| #define REG_MODEM_CONFIG_2_7X       0x1e | ||||
| #define REG_PREAMBLE_MSB_7X         0x20 | ||||
| #define REG_PREAMBLE_LSB_7X         0x21 | ||||
| #define REG_PAYLOAD_LENGTH_7X       0x22 | ||||
| #define REG_MODEM_CONFIG_3_7X       0x26 | ||||
| #define REG_FREQ_ERROR_MSB_7X       0x28 | ||||
| #define REG_FREQ_ERROR_MID_7X       0x29 | ||||
| #define REG_FREQ_ERROR_LSB_7X       0x2a | ||||
| #define REG_RSSI_WIDEBAND_7X        0x2c | ||||
| #define REG_DETECTION_OPTIMIZE_7X   0x31 | ||||
| #define REG_HIGH_BW_OPTIMIZE_1_7X   0x36 | ||||
| #define REG_DETECTION_THRESHOLD_7X  0x37 | ||||
| #define REG_SYNC_WORD_7X            0x39 | ||||
| #define REG_HIGH_BW_OPTIMIZE_2_7X   0x3a | ||||
| #define REG_DIO_MAPPING_1_7X        0x40 | ||||
| #define REG_VERSION_7X              0x42 | ||||
| #define REG_TCXO_7X                 0x4b | ||||
| #define REG_PA_DAC_7X               0x4d | ||||
| 
 | ||||
| // Modes
 | ||||
| #define MODE_LONG_RANGE_MODE_7X     0x80 | ||||
| #define MODE_SLEEP_7X               0x00 | ||||
| #define MODE_STDBY_7X               0x01 | ||||
| #define MODE_TX_7X                  0x03 | ||||
| #define MODE_RX_CONTINUOUS_7X       0x05 | ||||
| #define MODE_RX_SINGLE_7X           0x06 | ||||
| 
 | ||||
| // PA config
 | ||||
| #define PA_BOOST_7X                 0x80 | ||||
| 
 | ||||
| // IRQ masks
 | ||||
| #define IRQ_TX_DONE_MASK_7X         0x08 | ||||
| #define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20 | ||||
| #define IRQ_RX_DONE_MASK_7X         0x40 | ||||
| 
 | ||||
| extern SPIClass SPI; | ||||
| 
 | ||||
| #define MAX_PKT_LENGTH           255 | ||||
| 
 | ||||
| sx127x::sx127x() : | ||||
|   _spiSettings(8E6, MSBFIRST, SPI_MODE0), | ||||
|   _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), | ||||
|   _frequency(0), | ||||
|   _packetIndex(0), | ||||
|   _preinit_done(false), | ||||
|   _onReceive(NULL) | ||||
| { | ||||
|   // overide Stream timeout value
 | ||||
|   setTimeout(0); | ||||
| } | ||||
| 
 | ||||
| bool sx127x::preInit() { | ||||
|   // setup pins
 | ||||
|   pinMode(_ss, OUTPUT); | ||||
|   // set SS high
 | ||||
|   digitalWrite(_ss, HIGH); | ||||
|    | ||||
|   SPI.begin(); | ||||
| 
 | ||||
|   // check version (retry for up to 2 seconds)
 | ||||
|   uint8_t version; | ||||
|   long start = millis(); | ||||
|   while (((millis() - start) < 2000) && (millis() >= start)) { | ||||
|       version = readRegister(REG_VERSION_7X); | ||||
|       if (version == 0x12) { | ||||
|           break; | ||||
|       } | ||||
|       delay(100); | ||||
|   } | ||||
|   if (version != 0x12) { | ||||
|       return false; | ||||
|   } | ||||
| 
 | ||||
|   _preinit_done = true; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx127x::readRegister(uint8_t address) | ||||
| { | ||||
|   return singleTransfer(address & 0x7f, 0x00); | ||||
| } | ||||
| 
 | ||||
| void sx127x::writeRegister(uint8_t address, uint8_t value) | ||||
| { | ||||
|   singleTransfer(address | 0x80, value); | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) | ||||
| { | ||||
|   uint8_t response; | ||||
| 
 | ||||
|   digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|   SPI.beginTransaction(_spiSettings); | ||||
|   SPI.transfer(address); | ||||
|   response = SPI.transfer(value); | ||||
|   SPI.endTransaction(); | ||||
| 
 | ||||
|   digitalWrite(_ss, HIGH); | ||||
| 
 | ||||
|   return response; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int sx127x::begin(long frequency) | ||||
| { | ||||
|   if (_reset != -1) { | ||||
|     pinMode(_reset, OUTPUT); | ||||
| 
 | ||||
|     // perform reset
 | ||||
|     digitalWrite(_reset, LOW); | ||||
|     delay(10); | ||||
|     digitalWrite(_reset, HIGH); | ||||
|     delay(10); | ||||
|   } | ||||
| 
 | ||||
|   if (_busy != -1) { | ||||
|       pinMode(_busy, INPUT); | ||||
|   } | ||||
| 
 | ||||
|   if (!_preinit_done) { | ||||
|     if (!preInit()) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // put in sleep mode
 | ||||
|   sleep(); | ||||
| 
 | ||||
|   // set frequency
 | ||||
|   setFrequency(frequency); | ||||
| 
 | ||||
|   // set base addresses
 | ||||
|   writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0); | ||||
|   writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0); | ||||
| 
 | ||||
|   // set LNA boost
 | ||||
|   writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03); | ||||
| 
 | ||||
|   // set auto AGC
 | ||||
|   writeRegister(REG_MODEM_CONFIG_3_7X, 0x04); | ||||
| 
 | ||||
|   // set output power to 2 dBm
 | ||||
|   setTxPower(2); | ||||
| 
 | ||||
|   // put in standby mode
 | ||||
|   idle(); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| void sx127x::end() | ||||
| { | ||||
|   // put in sleep mode
 | ||||
|   sleep(); | ||||
| 
 | ||||
|   // stop SPI
 | ||||
|   SPI.end(); | ||||
| 
 | ||||
|   _preinit_done = false; | ||||
| } | ||||
| 
 | ||||
| int sx127x::beginPacket(int implicitHeader) | ||||
| { | ||||
|   // put in standby mode
 | ||||
|   idle(); | ||||
| 
 | ||||
|   if (implicitHeader) { | ||||
|     implicitHeaderMode(); | ||||
|   } else { | ||||
|     explicitHeaderMode(); | ||||
|   } | ||||
| 
 | ||||
|   // reset FIFO address and payload length
 | ||||
|   writeRegister(REG_FIFO_ADDR_PTR_7X, 0); | ||||
|   writeRegister(REG_PAYLOAD_LENGTH_7X, 0); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| int sx127x::endPacket() | ||||
| { | ||||
|   // put in TX mode
 | ||||
|   writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X); | ||||
| 
 | ||||
|   // wait for TX done
 | ||||
|   while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) { | ||||
|     yield(); | ||||
|   } | ||||
| 
 | ||||
|   // clear IRQ's
 | ||||
|   writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X); | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx127x::modemStatus() { | ||||
|     return readRegister(REG_MODEM_STAT_7X); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| uint8_t sx127x::currentRssiRaw() { | ||||
|     uint8_t rssi = readRegister(REG_RSSI_VALUE_7X); | ||||
|     return rssi; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx127x::currentRssi() { | ||||
|     int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET; | ||||
|     if (_frequency < 820E6) rssi -= 7; | ||||
|     return rssi; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx127x::packetRssiRaw() { | ||||
|     uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X); | ||||
|     return pkt_rssi_value; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx127x::packetRssi() { | ||||
|     int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET; | ||||
|     int pkt_snr = packetSnr(); | ||||
| 
 | ||||
|     if (_frequency < 820E6) pkt_rssi -= 7; | ||||
| 
 | ||||
|     if (pkt_snr < 0) { | ||||
|         pkt_rssi += pkt_snr; | ||||
|     } else { | ||||
|         // Slope correction is (16/15)*pkt_rssi,
 | ||||
|         // this estimation looses one floating point
 | ||||
|         // operation, and should be precise enough.
 | ||||
|         pkt_rssi = (int)(1.066 * pkt_rssi); | ||||
|     } | ||||
|     return pkt_rssi; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx127x::packetSnrRaw() { | ||||
|     return readRegister(REG_PKT_SNR_VALUE_7X); | ||||
| } | ||||
| 
 | ||||
| float ISR_VECT sx127x::packetSnr() { | ||||
|     return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; | ||||
| } | ||||
| 
 | ||||
| long sx127x::packetFrequencyError() | ||||
| { | ||||
|   int32_t freqError = 0; | ||||
|   freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111); | ||||
|   freqError <<= 8L; | ||||
|   freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X)); | ||||
|   freqError <<= 8L; | ||||
|   freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X)); | ||||
| 
 | ||||
|   if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
 | ||||
|       freqError -= 524288; // B1000'0000'0000'0000'0000
 | ||||
|   } | ||||
| 
 | ||||
|   const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
 | ||||
|   const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
 | ||||
| 
 | ||||
|   return static_cast<long>(fError); | ||||
| } | ||||
| 
 | ||||
| size_t sx127x::write(uint8_t byte) | ||||
| { | ||||
|   return write(&byte, sizeof(byte)); | ||||
| } | ||||
| 
 | ||||
| size_t sx127x::write(const uint8_t *buffer, size_t size) | ||||
| { | ||||
|     int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X); | ||||
| 
 | ||||
|     // check size
 | ||||
|     if ((currentLength + size) > MAX_PKT_LENGTH) { | ||||
|         size = MAX_PKT_LENGTH - currentLength; | ||||
|     } | ||||
| 
 | ||||
|     // write data
 | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         writeRegister(REG_FIFO_7X, buffer[i]); | ||||
|     } | ||||
| 
 | ||||
|     // update length
 | ||||
|     writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size); | ||||
|     return size; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx127x::available() | ||||
| { | ||||
|     return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx127x::read() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   _packetIndex++; | ||||
|   return readRegister(REG_FIFO_7X); | ||||
| } | ||||
| 
 | ||||
| int sx127x::peek() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   // store current FIFO address
 | ||||
|   int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X); | ||||
| 
 | ||||
|   // read
 | ||||
|   uint8_t b = readRegister(REG_FIFO_7X); | ||||
| 
 | ||||
|   // restore FIFO address
 | ||||
|   writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress); | ||||
| 
 | ||||
|   return b; | ||||
| } | ||||
| 
 | ||||
| void sx127x::flush() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void sx127x::onReceive(void(*callback)(int)) | ||||
| { | ||||
|   _onReceive = callback; | ||||
| 
 | ||||
|   if (callback) { | ||||
|     pinMode(_dio0, INPUT); | ||||
| 
 | ||||
|     writeRegister(REG_DIO_MAPPING_1_7X, 0x00); | ||||
| #ifdef SPI_HAS_NOTUSINGINTERRUPT | ||||
|     SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #endif | ||||
|     attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING); | ||||
|   } else { | ||||
|     detachInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #ifdef SPI_HAS_NOTUSINGINTERRUPT | ||||
|     SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| #endif | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx127x::receive(int size) | ||||
| { | ||||
|     if (size > 0) { | ||||
|         implicitHeaderMode(); | ||||
| 
 | ||||
|         writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff); | ||||
|     } else { | ||||
|         explicitHeaderMode(); | ||||
|     } | ||||
| 
 | ||||
|     writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X); | ||||
| } | ||||
| 
 | ||||
| void sx127x::idle() | ||||
| { | ||||
|     writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); | ||||
| } | ||||
| 
 | ||||
| void sx127x::sleep() | ||||
| { | ||||
|     writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); | ||||
| } | ||||
| 
 | ||||
| void sx127x::enableTCXO() { | ||||
|     uint8_t tcxo_reg = readRegister(REG_TCXO_7X); | ||||
|     writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); | ||||
| } | ||||
| 
 | ||||
| void sx127x::disableTCXO() { | ||||
|       uint8_t tcxo_reg = readRegister(REG_TCXO_7X); | ||||
|       writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); | ||||
| } | ||||
| 
 | ||||
| void sx127x::setTxPower(int level, int outputPin) { | ||||
|     if (PA_OUTPUT_RFO_PIN == outputPin) { | ||||
|         // RFO
 | ||||
|         if (level < 0) { | ||||
|             level = 0; | ||||
|         } else if (level > 14) { | ||||
|             level = 14; | ||||
|         } | ||||
| 
 | ||||
|         writeRegister(REG_PA_DAC_7X, 0x84); | ||||
|         writeRegister(REG_PA_CONFIG_7X, 0x70 | level); | ||||
| 
 | ||||
|     } else { | ||||
|         // PA BOOST
 | ||||
|         if (level < 2) { | ||||
|             level = 2; | ||||
|         } else if (level > 17) { | ||||
|             level = 17; | ||||
|         } | ||||
| 
 | ||||
|         writeRegister(REG_PA_DAC_7X, 0x84); | ||||
|         writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint8_t sx127x::getTxPower() { | ||||
|     byte txp = readRegister(REG_PA_CONFIG_7X); | ||||
|     return txp; | ||||
| } | ||||
| 
 | ||||
| void sx127x::setFrequency(unsigned long frequency) { | ||||
|   _frequency = frequency; | ||||
| 
 | ||||
|   uint32_t frf = ((uint64_t)frequency << 19) / 32000000; | ||||
| 
 | ||||
|   writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16)); | ||||
|   writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8)); | ||||
|   writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0)); | ||||
| 
 | ||||
|   optimizeModemSensitivity(); | ||||
| } | ||||
| 
 | ||||
| uint32_t sx127x::getFrequency() { | ||||
|     uint8_t msb = readRegister(REG_FRF_MSB_7X); | ||||
|     uint8_t mid = readRegister(REG_FRF_MID_7X); | ||||
|     uint8_t lsb = readRegister(REG_FRF_LSB_7X); | ||||
| 
 | ||||
|     uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb; | ||||
|     uint64_t frm = (uint64_t)frf*32000000; | ||||
|     uint32_t frequency = (frm >> 19); | ||||
| 
 | ||||
|     return frequency; | ||||
| } | ||||
| 
 | ||||
| void sx127x::setSpreadingFactor(int sf) | ||||
| { | ||||
|   if (sf < 6) { | ||||
|     sf = 6; | ||||
|   } else if (sf > 12) { | ||||
|     sf = 12; | ||||
|   } | ||||
| 
 | ||||
|   if (sf == 6) { | ||||
|       writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5); | ||||
|       writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c); | ||||
|   } else { | ||||
|       writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3); | ||||
|       writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a); | ||||
|   } | ||||
| 
 | ||||
|   writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0)); | ||||
|   handleLowDataRate(); | ||||
| } | ||||
| 
 | ||||
| long sx127x::getSignalBandwidth() | ||||
| { | ||||
|       byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4); | ||||
|       switch (bw) { | ||||
|         case 0: return 7.8E3; | ||||
|         case 1: return 10.4E3; | ||||
|         case 2: return 15.6E3; | ||||
|         case 3: return 20.8E3; | ||||
|         case 4: return 31.25E3; | ||||
|         case 5: return 41.7E3; | ||||
|         case 6: return 62.5E3; | ||||
|         case 7: return 125E3; | ||||
|         case 8: return 250E3; | ||||
|         case 9: return 500E3; | ||||
|       } | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| void sx127x::handleLowDataRate(){ | ||||
|     int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4); | ||||
|     if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) { | ||||
|         // set auto AGC and LowDataRateOptimize
 | ||||
|         writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2)); | ||||
|     } else { | ||||
|         // set auto AGC
 | ||||
|         writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx127x::optimizeModemSensitivity(){ | ||||
|     byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4); | ||||
|     uint32_t freq = getFrequency(); | ||||
| 
 | ||||
|     if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) { | ||||
|         writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); | ||||
|         writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f); | ||||
|     } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) { | ||||
|         writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); | ||||
|         writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64); | ||||
|     } else { | ||||
|         writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx127x::setSignalBandwidth(long sbw) | ||||
| { | ||||
|     int bw; | ||||
| 
 | ||||
|     if (sbw <= 7.8E3) { | ||||
|         bw = 0; | ||||
|     } else if (sbw <= 10.4E3) { | ||||
|         bw = 1; | ||||
|     } else if (sbw <= 15.6E3) { | ||||
|         bw = 2; | ||||
|     } else if (sbw <= 20.8E3) { | ||||
|         bw = 3; | ||||
|     } else if (sbw <= 31.25E3) { | ||||
|         bw = 4; | ||||
|     } else if (sbw <= 41.7E3) { | ||||
|         bw = 5; | ||||
|     } else if (sbw <= 62.5E3) { | ||||
|         bw = 6; | ||||
|     } else if (sbw <= 125E3) { | ||||
|         bw = 7; | ||||
|     } else if (sbw <= 250E3) { | ||||
|         bw = 8; | ||||
|     } else /*if (sbw <= 250E3)*/ { | ||||
|         bw = 9; | ||||
|     } | ||||
| 
 | ||||
|     writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4)); | ||||
| 
 | ||||
| 
 | ||||
|   handleLowDataRate(); | ||||
|   optimizeModemSensitivity(); | ||||
| } | ||||
| 
 | ||||
| void sx127x::setCodingRate4(int denominator) | ||||
| { | ||||
|   if (denominator < 5) { | ||||
|     denominator = 5; | ||||
|   } else if (denominator > 8) { | ||||
|     denominator = 8; | ||||
|   } | ||||
| 
 | ||||
|   int cr = denominator - 4; | ||||
| 
 | ||||
|   writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1)); | ||||
| } | ||||
| 
 | ||||
| void sx127x::setPreambleLength(long length) | ||||
| { | ||||
|   writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8)); | ||||
|   writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0)); | ||||
| } | ||||
| 
 | ||||
| void sx127x::setSyncWord(int sw) | ||||
| { | ||||
|     writeRegister(REG_SYNC_WORD_7X, sw); | ||||
| } | ||||
| 
 | ||||
| void sx127x::enableCrc() | ||||
| { | ||||
|     writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); | ||||
| } | ||||
| 
 | ||||
| void sx127x::disableCrc() | ||||
| { | ||||
|     writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); | ||||
| } | ||||
| 
 | ||||
| byte sx127x::random() | ||||
| { | ||||
|     return readRegister(REG_RSSI_WIDEBAND_7X); | ||||
| } | ||||
| 
 | ||||
| void sx127x::setPins(int ss, int reset, int dio0, int busy) | ||||
| { | ||||
|   _ss = ss; | ||||
|   _reset = reset; | ||||
|   _dio0 = dio0; | ||||
|   _busy = busy; | ||||
| } | ||||
| 
 | ||||
| void sx127x::setSPIFrequency(uint32_t frequency) | ||||
| { | ||||
|   _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); | ||||
| } | ||||
| 
 | ||||
| void sx127x::dumpRegisters(Stream& out) | ||||
| { | ||||
|   for (int i = 0; i < 128; i++) { | ||||
|     out.print("0x"); | ||||
|     out.print(i, HEX); | ||||
|     out.print(": 0x"); | ||||
|     out.println(readRegister(i), HEX); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx127x::explicitHeaderMode() | ||||
| { | ||||
|   _implicitHeaderMode = 0; | ||||
| 
 | ||||
|   writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); | ||||
| } | ||||
| 
 | ||||
| void sx127x::implicitHeaderMode() | ||||
| { | ||||
|   _implicitHeaderMode = 1; | ||||
| 
 | ||||
|   writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void ISR_VECT sx127x::handleDio0Rise() | ||||
| { | ||||
|     int irqFlags = readRegister(REG_IRQ_FLAGS_7X); | ||||
| 
 | ||||
|     // clear IRQ's
 | ||||
|     writeRegister(REG_IRQ_FLAGS_7X, irqFlags); | ||||
|     if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) { | ||||
| 
 | ||||
|         // received a packet
 | ||||
|         _packetIndex = 0; | ||||
| 
 | ||||
|         // read packet length
 | ||||
|         int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X); | ||||
| 
 | ||||
|         // set FIFO address to current RX address
 | ||||
|         writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X)); | ||||
| 
 | ||||
|         if (_onReceive) { | ||||
|             _onReceive(packetLength); | ||||
|         } | ||||
| 
 | ||||
|         // reset FIFO address
 | ||||
|         writeRegister(REG_FIFO_ADDR_PTR_7X, 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ISR_VECT sx127x::onDio0Rise() | ||||
| { | ||||
|   sx127x_modem.handleDio0Rise(); | ||||
| } | ||||
| 
 | ||||
| sx127x sx127x_modem; | ||||
							
								
								
									
										117
									
								
								sx127x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								sx127x.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| // Copyright (c) Sandeep Mistry. All rights reserved.
 | ||||
| // Licensed under the MIT license.
 | ||||
| 
 | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #ifndef SX1276_H | ||||
| #define SX1276_H | ||||
| 
 | ||||
| #include <Arduino.h> | ||||
| #include <SPI.h> | ||||
| #include "Modem.h" | ||||
| 
 | ||||
| #define LORA_DEFAULT_SS_PIN    10 | ||||
| #define LORA_DEFAULT_RESET_PIN 9 | ||||
| #define LORA_DEFAULT_DIO0_PIN  2 | ||||
| #define LORA_DEFAULT_BUSY_PIN  -1 | ||||
| 
 | ||||
| #define PA_OUTPUT_RFO_PIN      0 | ||||
| #define PA_OUTPUT_PA_BOOST_PIN 1 | ||||
| 
 | ||||
| #define RSSI_OFFSET 157 | ||||
| 
 | ||||
| class sx127x : public Stream { | ||||
| public: | ||||
|   sx127x(); | ||||
| 
 | ||||
|   int begin(long frequency); | ||||
|   void end(); | ||||
| 
 | ||||
|   int beginPacket(int implicitHeader = false); | ||||
|   int endPacket(); | ||||
| 
 | ||||
|   int parsePacket(int size = 0); | ||||
|   int packetRssi(); | ||||
|   int currentRssi(); | ||||
|   uint8_t packetRssiRaw(); | ||||
|   uint8_t currentRssiRaw(); | ||||
|   uint8_t packetSnrRaw(); | ||||
|   float packetSnr(); | ||||
|   long packetFrequencyError(); | ||||
| 
 | ||||
|   // from Print
 | ||||
|   virtual size_t write(uint8_t byte); | ||||
|   virtual size_t write(const uint8_t *buffer, size_t size); | ||||
| 
 | ||||
|   // from Stream
 | ||||
|   virtual int available(); | ||||
|   virtual int read(); | ||||
|   virtual int peek(); | ||||
|   virtual void flush(); | ||||
| 
 | ||||
|   void onReceive(void(*callback)(int)); | ||||
| 
 | ||||
|   void receive(int size = 0); | ||||
|   void idle(); | ||||
|   void sleep(); | ||||
| 
 | ||||
|   bool preInit(); | ||||
|   uint8_t getTxPower(); | ||||
|   void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); | ||||
|   uint32_t getFrequency(); | ||||
|   void setFrequency(unsigned long frequency); | ||||
|   void setSpreadingFactor(int sf); | ||||
|   long getSignalBandwidth(); | ||||
|   void setSignalBandwidth(long sbw); | ||||
|   void setCodingRate4(int denominator); | ||||
|   void setPreambleLength(long length); | ||||
|   void setSyncWord(int sw); | ||||
|   uint8_t modemStatus(); | ||||
|   void enableCrc(); | ||||
|   void disableCrc(); | ||||
|   void enableTCXO(); | ||||
|   void disableTCXO(); | ||||
| 
 | ||||
|   // deprecated
 | ||||
|   void crc() { enableCrc(); } | ||||
|   void noCrc() { disableCrc(); } | ||||
| 
 | ||||
|   byte random(); | ||||
| 
 | ||||
|   void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN); | ||||
|   void setSPIFrequency(uint32_t frequency); | ||||
| 
 | ||||
|   void dumpRegisters(Stream& out); | ||||
| 
 | ||||
| private: | ||||
|   void explicitHeaderMode(); | ||||
|   void implicitHeaderMode(); | ||||
| 
 | ||||
|   void handleDio0Rise(); | ||||
| 
 | ||||
|   uint8_t readRegister(uint8_t address); | ||||
|   void writeRegister(uint8_t address, uint8_t value); | ||||
|   uint8_t singleTransfer(uint8_t address, uint8_t value); | ||||
| 
 | ||||
|   static void onDio0Rise(); | ||||
| 
 | ||||
|   void handleLowDataRate(); | ||||
|   void optimizeModemSensitivity(); | ||||
| 
 | ||||
| private: | ||||
|   SPISettings _spiSettings; | ||||
|   int _ss; | ||||
|   int _reset; | ||||
|   int _dio0; | ||||
|   int _busy; | ||||
|   long _frequency; | ||||
|   int _packetIndex; | ||||
|   int _implicitHeaderMode; | ||||
|   bool _preinit_done; | ||||
|   void (*_onReceive)(int); | ||||
| }; | ||||
| 
 | ||||
| extern sx127x sx127x_modem; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										882
									
								
								sx128x.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										882
									
								
								sx128x.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,882 @@ | ||||
| // Copyright (c) Sandeep Mistry. All rights reserved.
 | ||||
| // Licensed under the MIT license.
 | ||||
| 
 | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #include "sx128x.h" | ||||
| 
 | ||||
| #define MCU_1284P 0x91 | ||||
| #define MCU_2560  0x92 | ||||
| #define MCU_ESP32 0x81 | ||||
| #define MCU_NRF52 0x71 | ||||
| #if defined(__AVR_ATmega1284P__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_1284P | ||||
| #elif defined(__AVR_ATmega2560__) | ||||
|   #define PLATFORM PLATFORM_AVR | ||||
|   #define MCU_VARIANT MCU_2560 | ||||
| #elif defined(ESP32) | ||||
|   #define PLATFORM PLATFORM_ESP32 | ||||
|   #define MCU_VARIANT MCU_ESP32 | ||||
| #elif defined(NRF52840_XXAA) | ||||
|   #define PLATFORM PLATFORM_NRF52 | ||||
|   #define MCU_VARIANT MCU_NRF52 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef MCU_VARIANT | ||||
|   #error No MCU variant defined, cannot compile | ||||
| #endif | ||||
| 
 | ||||
| #if MCU_VARIANT == MCU_ESP32 | ||||
|   #include "soc/rtc_wdt.h" | ||||
|   #define ISR_VECT IRAM_ATTR | ||||
| #else | ||||
|   #define ISR_VECT | ||||
| #endif | ||||
| 
 | ||||
| #define OP_RF_FREQ_8X               0x86 | ||||
| #define OP_SLEEP_8X                 0x84 | ||||
| #define OP_STANDBY_8X               0x80 | ||||
| #define OP_TX_8X                    0x83 | ||||
| #define OP_RX_8X                    0x82 | ||||
| #define OP_SET_IRQ_FLAGS_8X         0x8D // also provides info such as
 | ||||
|                                          // preamble detection, etc for
 | ||||
|                                          // knowing when it's safe to switch
 | ||||
|                                          // antenna modes
 | ||||
| #define OP_CLEAR_IRQ_STATUS_8X      0x97 | ||||
| #define OP_GET_IRQ_STATUS_8X        0x15 | ||||
| #define OP_RX_BUFFER_STATUS_8X      0x17 | ||||
| #define OP_PACKET_STATUS_8X         0x1D // get snr & rssi of last packet
 | ||||
| #define OP_CURRENT_RSSI_8X          0x1F | ||||
| #define OP_MODULATION_PARAMS_8X     0x8B // bw, sf, cr, etc.
 | ||||
| #define OP_PACKET_PARAMS_8X         0x8C // crc, preamble, payload length, etc.
 | ||||
| #define OP_STATUS_8X                0xC0 | ||||
| #define OP_TX_PARAMS_8X             0x8E // set dbm, etc
 | ||||
| #define OP_PACKET_TYPE_8X           0x8A | ||||
| #define OP_BUFFER_BASE_ADDR_8X      0x8F | ||||
| #define OP_READ_REGISTER_8X         0x19 | ||||
| #define OP_WRITE_REGISTER_8X        0x18 | ||||
| #define IRQ_TX_DONE_MASK_8X         0x01 | ||||
| #define IRQ_RX_DONE_MASK_8X         0x02 | ||||
| #define IRQ_HEADER_DET_MASK_8X      0x10 | ||||
| #define IRQ_HEADER_ERROR_MASK_8X    0x20 | ||||
| #define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40 | ||||
| 
 | ||||
| #define MODE_LONG_RANGE_MODE_8X     0x01 | ||||
| 
 | ||||
| #define OP_FIFO_WRITE_8X            0x1A | ||||
| #define OP_FIFO_READ_8X             0x1B | ||||
| #define IRQ_PREAMBLE_DET_MASK_8X    0x80 | ||||
| 
 | ||||
| #define REG_PACKET_SIZE            0x901 | ||||
| #define REG_FIRM_VER_MSB           0x154 | ||||
| #define REG_FIRM_VER_LSB           0x153 | ||||
| 
 | ||||
| #define XTAL_FREQ_8X (double)52000000 | ||||
| #define FREQ_DIV_8X (double)pow(2.0, 18.0) | ||||
| #define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X) | ||||
| 
 | ||||
| extern SPIClass spiModem; | ||||
| #define SPI spiModem | ||||
| 
 | ||||
| extern SPIClass SPI; | ||||
| 
 | ||||
| #define MAX_PKT_LENGTH           255 | ||||
| 
 | ||||
| sx128x::sx128x() : | ||||
|   _spiSettings(8E6, MSBFIRST, SPI_MODE0), | ||||
|   _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN), | ||||
|   _frequency(0), | ||||
|   _txp(0), | ||||
|   _sf(0x50), | ||||
|   _bw(0x34), | ||||
|   _cr(0x01), | ||||
|   _packetIndex(0), | ||||
|   _preambleLength(18), | ||||
|   _implicitHeaderMode(0), | ||||
|   _payloadLength(255), | ||||
|   _crcMode(0), | ||||
|   _fifo_tx_addr_ptr(0), | ||||
|   _fifo_rx_addr_ptr(0), | ||||
|   _packet({0}), | ||||
|   _rxPacketLength(0), | ||||
|   _preinit_done(false), | ||||
|   _onReceive(NULL) | ||||
| { | ||||
|   // overide Stream timeout value
 | ||||
|   setTimeout(0); | ||||
| } | ||||
| 
 | ||||
| bool sx128x::preInit() { | ||||
|   // setup pins
 | ||||
|   pinMode(_ss, OUTPUT); | ||||
|   // set SS high
 | ||||
|   digitalWrite(_ss, HIGH); | ||||
|    | ||||
|   SPI.begin(); | ||||
| 
 | ||||
|   // check version (retry for up to 2 seconds)
 | ||||
|   long start = millis(); | ||||
| 
 | ||||
|   uint8_t version_msb; | ||||
|   uint8_t version_lsb; | ||||
| 
 | ||||
|   while (((millis() - start) < 2000) && (millis() >= start)) { | ||||
| 
 | ||||
|       version_msb = readRegister(REG_FIRM_VER_MSB); | ||||
|       version_lsb = readRegister(REG_FIRM_VER_LSB); | ||||
| 
 | ||||
|       if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { | ||||
|           break; | ||||
|       } | ||||
|       delay(100); | ||||
|   } | ||||
|   if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { | ||||
|       return false; | ||||
|   } | ||||
| 
 | ||||
|   _preinit_done = true; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx128x::readRegister(uint16_t address) | ||||
| { | ||||
|   return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); | ||||
| } | ||||
| 
 | ||||
| void sx128x::writeRegister(uint16_t address, uint8_t value) | ||||
| { | ||||
|     singleTransfer(OP_WRITE_REGISTER_8X, address, value); | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     uint8_t response; | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
|     SPI.transfer((address & 0xFF00) >> 8); | ||||
|     SPI.transfer(address & 0x00FF); | ||||
|     if (opcode == OP_READ_REGISTER_8X) { | ||||
|         SPI.transfer(0x00); | ||||
|     } | ||||
|     response = SPI.transfer(value); | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| 
 | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| void sx128x::rxAntEnable() | ||||
| { | ||||
|     if (_txen != -1) { | ||||
|         digitalWrite(_txen, LOW); | ||||
|     } | ||||
|     if (_rxen != -1) { | ||||
|         digitalWrite(_rxen, HIGH); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx128x::txAntEnable() | ||||
| { | ||||
|     if (_txen != -1) { | ||||
|         digitalWrite(_txen, HIGH); | ||||
|     } | ||||
|     if (_rxen != -1) { | ||||
|         digitalWrite(_rxen, LOW); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx128x::loraMode() { | ||||
|     // enable lora mode on the SX1262 chip
 | ||||
|     uint8_t mode = MODE_LONG_RANGE_MODE_8X; | ||||
|     executeOpcode(OP_PACKET_TYPE_8X, &mode, 1); | ||||
| } | ||||
| 
 | ||||
| void sx128x::waitOnBusy() { | ||||
|     unsigned long time = millis(); | ||||
|     if (_busy != -1) { | ||||
|         while (digitalRead(_busy) == HIGH) | ||||
|         { | ||||
|             if (millis() >= (time + 300)) { | ||||
|                 pinMode(LED_BLUE, OUTPUT); | ||||
|                 digitalWrite(LED_BLUE, HIGH); | ||||
|                 break; | ||||
|             } | ||||
|             // do nothing
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         SPI.transfer(buffer[i]); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(opcode); | ||||
|     SPI.transfer(0x00); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         buffer[i] = SPI.transfer(0x00); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx128x::writeBuffer(const uint8_t* buffer, size_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(OP_FIFO_WRITE_8X); | ||||
|     SPI.transfer(_fifo_tx_addr_ptr); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         SPI.transfer(buffer[i]); | ||||
|         _fifo_tx_addr_ptr++; | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx128x::readBuffer(uint8_t* buffer, size_t size) | ||||
| { | ||||
|     waitOnBusy(); | ||||
| 
 | ||||
|     digitalWrite(_ss, LOW); | ||||
| 
 | ||||
|     SPI.beginTransaction(_spiSettings); | ||||
|     SPI.transfer(OP_FIFO_READ_8X); | ||||
|     SPI.transfer(_fifo_rx_addr_ptr); | ||||
|     SPI.transfer(0x00); | ||||
| 
 | ||||
|     for (int i = 0; i < size; i++) | ||||
|     { | ||||
|         buffer[i] = SPI.transfer(0x00); | ||||
|     } | ||||
| 
 | ||||
|     SPI.endTransaction(); | ||||
| 
 | ||||
|     digitalWrite(_ss, HIGH); | ||||
| } | ||||
| 
 | ||||
| void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) { | ||||
|   // because there is no access to these registers on the sx1280, we have
 | ||||
|   // to set all these parameters at once or not at all.
 | ||||
|   uint8_t buf[3]; | ||||
| 
 | ||||
|   buf[0] = sf; | ||||
|   buf[1] = bw; | ||||
|   buf[2] = cr;  | ||||
|   executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3); | ||||
| 
 | ||||
|   if (sf <= 6) { | ||||
|       writeRegister(0x925, 0x1E); | ||||
|   } else if (sf <= 8) { | ||||
|       writeRegister(0x925, 0x37); | ||||
|   } else if (sf >= 9) { | ||||
|       writeRegister(0x925, 0x32); | ||||
|   } | ||||
|   writeRegister(0x093C, 0x1); | ||||
| } | ||||
| 
 | ||||
| void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) { | ||||
|   // because there is no access to these registers on the sx1280, we have
 | ||||
|   // to set all these parameters at once or not at all.
 | ||||
|   uint8_t buf[7]; | ||||
| 
 | ||||
|   // calculate exponent and mantissa values for modem
 | ||||
|   uint8_t e = 1; | ||||
|   uint8_t m = 1; | ||||
|   uint32_t preamblelen; | ||||
| 
 | ||||
|   for (e <= 15; e++;) { | ||||
|       for (m <= 15; m++;) { | ||||
|           preamblelen = m * (uint32_t(1) << e); | ||||
|           if (preamblelen >= preamble) break; | ||||
|       } | ||||
|       if (preamblelen >= preamble) break; | ||||
|   } | ||||
| 
 | ||||
|   buf[0] = (e << 4) | m; | ||||
|   buf[1] = headermode; | ||||
|   buf[2] = length; | ||||
|   buf[3] = crc; | ||||
|   // standard IQ setting (no inversion)
 | ||||
|   buf[4] = 0x40;  | ||||
|   // unused params
 | ||||
|   buf[5] = 0x00;  | ||||
|   buf[6] = 0x00;  | ||||
| 
 | ||||
|   executeOpcode(OP_PACKET_PARAMS_8X, buf, 7); | ||||
| } | ||||
| 
 | ||||
| int sx128x::begin(unsigned long frequency) | ||||
| { | ||||
|   if (_reset != -1) { | ||||
|     pinMode(_reset, OUTPUT); | ||||
| 
 | ||||
|     // perform reset
 | ||||
|     digitalWrite(_reset, LOW); | ||||
|     delay(10); | ||||
|     digitalWrite(_reset, HIGH); | ||||
|     delay(10); | ||||
|   } | ||||
| 
 | ||||
|   if (_busy != -1) { | ||||
|       pinMode(_busy, INPUT); | ||||
|   } | ||||
| 
 | ||||
|   if (!_preinit_done) { | ||||
|     if (!preInit()) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   idle(); | ||||
|   loraMode(); | ||||
|   rxAntEnable(); | ||||
| 
 | ||||
|   setFrequency(frequency); | ||||
| 
 | ||||
|   // set LNA boost
 | ||||
|   // todo: implement this
 | ||||
|   //writeRegister(REG_LNA, 0x96);
 | ||||
| 
 | ||||
|   setModulationParams(_sf, _bw, _cr); | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|   // set output power to 2 dBm
 | ||||
|   setTxPower(2); | ||||
| 
 | ||||
|   // set base addresses
 | ||||
|   uint8_t basebuf[2] = {0}; | ||||
|   executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| void sx128x::end() | ||||
| { | ||||
|   // put in sleep mode
 | ||||
|   sleep(); | ||||
| 
 | ||||
|   // stop SPI
 | ||||
|   SPI.end(); | ||||
| 
 | ||||
|   _preinit_done = false; | ||||
| } | ||||
| 
 | ||||
| int sx128x::beginPacket(int implicitHeader) | ||||
| { | ||||
|   // put in standby mode
 | ||||
|   idle(); | ||||
| 
 | ||||
|   if (implicitHeader) { | ||||
|     implicitHeaderMode(); | ||||
|   } else { | ||||
|     explicitHeaderMode(); | ||||
|   } | ||||
| 
 | ||||
|   _payloadLength = 0; | ||||
|   _fifo_tx_addr_ptr = 0; | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| int sx128x::endPacket() | ||||
| { | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| 
 | ||||
|   txAntEnable(); | ||||
| 
 | ||||
|   // put in single TX mode
 | ||||
|   uint8_t timeout[3] = {0}; | ||||
|   executeOpcode(OP_TX_8X, timeout, 3); | ||||
| 
 | ||||
|   uint8_t buf[2]; | ||||
| 
 | ||||
|   buf[0] = 0x00; | ||||
|   buf[1] = 0x00; | ||||
| 
 | ||||
|   executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); | ||||
| 
 | ||||
|   // wait for TX done
 | ||||
|   while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) { | ||||
|     buf[0] = 0x00; | ||||
|     buf[1] = 0x00; | ||||
|     executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); | ||||
|     yield(); | ||||
|   } | ||||
| 
 | ||||
|   // clear IRQ's
 | ||||
| 
 | ||||
|   uint8_t mask[2]; | ||||
|   mask[0] = 0x00; | ||||
|   mask[1] = IRQ_TX_DONE_MASK_8X; | ||||
|   executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2); | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx128x::modemStatus() { | ||||
|     // imitate the register status from the sx1276 / 78
 | ||||
|     uint8_t buf[2] = {0}; | ||||
| 
 | ||||
|     // debug
 | ||||
|     executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); | ||||
| 
 | ||||
|     uint8_t clearbuf[2] = {0}; | ||||
| 
 | ||||
|     uint8_t byte = 0x00; | ||||
| 
 | ||||
|     if (buf[0] & IRQ_PREAMBLE_DET_MASK_8X != 0) { | ||||
|         byte = byte | 0x01 | 0x04; | ||||
|         // clear register after reading
 | ||||
|         //clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X; 
 | ||||
|         clearbuf[0] = 0xFF; | ||||
|     } | ||||
| 
 | ||||
|     if (buf[1] & IRQ_HEADER_DET_MASK_8X != 0) { | ||||
|         byte = byte | 0x02 | 0x04; | ||||
|         // clear register after reading
 | ||||
|         //clearbuf[1] = IRQ_HEADER_DET_MASK_8X; 
 | ||||
|         clearbuf[1] = 0xFF; | ||||
|     } | ||||
| 
 | ||||
|     // debug
 | ||||
|     executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); | ||||
| 
 | ||||
|     return byte; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| uint8_t sx128x::currentRssiRaw() { | ||||
|     uint8_t byte = 0; | ||||
|     executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); | ||||
|     return byte; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx128x::currentRssi() { | ||||
|     uint8_t byte = 0; | ||||
|     executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); | ||||
|     int rssi = -byte / 2; | ||||
|     return rssi; | ||||
| } | ||||
| 
 | ||||
| uint8_t sx128x::packetRssiRaw() { | ||||
|     uint8_t buf[5] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); | ||||
|     return buf[0]; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx128x::packetRssi() { | ||||
|     // may need more calculations here
 | ||||
|     uint8_t buf[5] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); | ||||
|     int pkt_rssi = -buf[0] / 2; | ||||
|     return pkt_rssi; | ||||
| } | ||||
| 
 | ||||
| uint8_t ISR_VECT sx128x::packetSnrRaw() { | ||||
|     uint8_t buf[5] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); | ||||
|     return buf[1]; | ||||
| } | ||||
| 
 | ||||
| float ISR_VECT sx128x::packetSnr() { | ||||
|     uint8_t buf[5] = {0}; | ||||
|     executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3); | ||||
|     return float(buf[1]) * 0.25; | ||||
| } | ||||
| 
 | ||||
| long sx128x::packetFrequencyError() | ||||
| { | ||||
|   int32_t freqError = 0; | ||||
|   // todo: implement this, page 120 of sx1280 datasheet
 | ||||
|   const float fError = 0.0; | ||||
|   return static_cast<long>(fError); | ||||
| } | ||||
| 
 | ||||
| size_t sx128x::write(uint8_t byte) | ||||
| { | ||||
|   return write(&byte, sizeof(byte)); | ||||
| } | ||||
| 
 | ||||
| size_t sx128x::write(const uint8_t *buffer, size_t size) | ||||
| { | ||||
|   if ((_payloadLength + size) > MAX_PKT_LENGTH) { | ||||
|       size = MAX_PKT_LENGTH - _payloadLength; | ||||
|   } | ||||
| 
 | ||||
|   // write data
 | ||||
|   writeBuffer(buffer, size); | ||||
|   _payloadLength = _payloadLength + size; | ||||
|   return size; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx128x::available() | ||||
| { | ||||
|     return _rxPacketLength - _packetIndex; | ||||
| } | ||||
| 
 | ||||
| int ISR_VECT sx128x::read() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   uint8_t byte = _packet[_packetIndex]; | ||||
|   _packetIndex++; | ||||
|   return byte; | ||||
| } | ||||
| 
 | ||||
| int sx128x::peek() | ||||
| { | ||||
|   if (!available()) { | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   uint8_t b = _packet[_packetIndex]; | ||||
|   return b; | ||||
| } | ||||
| 
 | ||||
| void sx128x::flush() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void sx128x::onReceive(void(*callback)(int)) | ||||
| { | ||||
|   _onReceive = callback; | ||||
| 
 | ||||
|   if (callback) { | ||||
|     pinMode(_dio0, INPUT); | ||||
| 
 | ||||
|       // set preamble and header detection irqs, plus dio0 mask
 | ||||
|       uint8_t buf[8]; | ||||
| 
 | ||||
|       // set irq masks, enable all
 | ||||
|       buf[0] = 0xFF;  | ||||
|       buf[1] = 0xFF; | ||||
| 
 | ||||
|       // set dio0 masks
 | ||||
|       buf[2] = 0x00; | ||||
|       buf[3] = IRQ_RX_DONE_MASK_8X;  | ||||
| 
 | ||||
|       // set dio1 masks
 | ||||
|       buf[4] = 0x00;  | ||||
|       buf[5] = 0x00; | ||||
| 
 | ||||
|       // set dio2 masks
 | ||||
|       buf[6] = 0x00;  | ||||
|       buf[7] = 0x00; | ||||
| 
 | ||||
|       executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); | ||||
| //#ifdef SPI_HAS_NOTUSINGINTERRUPT
 | ||||
| //    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
 | ||||
| //#endif
 | ||||
|     attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING); | ||||
|   } else { | ||||
|     detachInterrupt(digitalPinToInterrupt(_dio0)); | ||||
| //#ifdef SPI_HAS_NOTUSINGINTERRUPT
 | ||||
| //    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
 | ||||
| //#endif
 | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx128x::receive(int size) | ||||
| { | ||||
|   if (size > 0) { | ||||
|     implicitHeaderMode(); | ||||
| 
 | ||||
|     // tell radio payload length
 | ||||
|     _rxPacketLength = size; | ||||
|     //_payloadLength = size;
 | ||||
|     //setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
 | ||||
|   } else { | ||||
|     explicitHeaderMode(); | ||||
|   } | ||||
| 
 | ||||
|   rxAntEnable(); | ||||
| 
 | ||||
|     uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
 | ||||
|     executeOpcode(OP_RX_8X, mode, 3); | ||||
| } | ||||
| 
 | ||||
| void sx128x::idle() | ||||
| { | ||||
|       #if HAS_TCXO | ||||
|           // STDBY_XOSC
 | ||||
|           uint8_t byte = 0x01; | ||||
|       #else | ||||
|           // STDBY_RC
 | ||||
|           uint8_t byte = 0x00; | ||||
|       #endif | ||||
|       executeOpcode(OP_STANDBY_8X, &byte, 1);  | ||||
| } | ||||
| 
 | ||||
| void sx128x::sleep() | ||||
| { | ||||
|     uint8_t byte = 0x00; | ||||
|     executeOpcode(OP_SLEEP_8X, &byte, 1); | ||||
| } | ||||
| 
 | ||||
| void sx128x::enableTCXO() { | ||||
|     // todo: need to check how to implement on sx1280
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::disableTCXO() { | ||||
|     // todo: need to check how to implement on sx1280
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::setTxPower(int level, int outputPin) { | ||||
|     if (level > 13) { | ||||
|         level = 13; | ||||
|     } else if (level < -18) { | ||||
|         level = -18; | ||||
|     } | ||||
| 
 | ||||
|     _txp = level; | ||||
| 
 | ||||
|     level = level + 18; | ||||
| 
 | ||||
|     uint8_t tx_buf[2]; | ||||
| 
 | ||||
|     tx_buf[0] = level; | ||||
|     tx_buf[1] = 0xE0; // ramping time - 20 microseconds
 | ||||
| 
 | ||||
|     executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); | ||||
| } | ||||
| 
 | ||||
| uint8_t sx128x::getTxPower() { | ||||
|       return _txp; | ||||
| } | ||||
| 
 | ||||
| void sx128x::setFrequency(unsigned long frequency) { | ||||
|   _frequency = frequency; | ||||
| 
 | ||||
|   uint8_t buf[3]; | ||||
| 
 | ||||
|   uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X); | ||||
| 
 | ||||
|   buf[0] = ((freq >> 16) & 0xFF); | ||||
|   buf[1] = ((freq >> 8) & 0xFF); | ||||
|   buf[2] = (freq & 0xFF); | ||||
| 
 | ||||
|   executeOpcode(OP_RF_FREQ_8X, buf, 3); | ||||
| } | ||||
| 
 | ||||
| uint32_t sx128x::getFrequency() { | ||||
|   // we can't read the frequency on the sx1280
 | ||||
|   uint32_t frequency = _frequency; | ||||
| 
 | ||||
|   return frequency; | ||||
| } | ||||
| 
 | ||||
| void sx128x::setSpreadingFactor(int sf) | ||||
| { | ||||
|   if (sf < 5) { | ||||
|       sf = 5; | ||||
|   } else if (sf > 12) { | ||||
|     sf = 12; | ||||
|   } | ||||
| 
 | ||||
|   _sf = sf << 4; | ||||
| 
 | ||||
|   setModulationParams(sf << 4, _bw, _cr); | ||||
|   handleLowDataRate(); | ||||
| } | ||||
| 
 | ||||
| long sx128x::getSignalBandwidth() | ||||
| { | ||||
|   int bw = _bw; | ||||
|   switch (bw) { | ||||
|       case 0x34: return 203.125E3; | ||||
|       case 0x26: return 406.25E3; | ||||
|       case 0x18: return 812.5E3; | ||||
|       case 0x0A: return 1625E3; | ||||
|   } | ||||
|    | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| void sx128x::handleLowDataRate(){ | ||||
|     // todo: do i need this??
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::optimizeModemSensitivity(){ | ||||
|     // todo: check if there's anything the sx1280 can do here
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::setSignalBandwidth(long sbw) | ||||
| { | ||||
|       if (sbw <= 203.125E3) { | ||||
|           _bw = 0x34; | ||||
|       } else if (sbw <= 406.25E3) { | ||||
|           _bw = 0x26; | ||||
|       } else if (sbw <= 812.5E3) { | ||||
|           _bw = 0x18; | ||||
|       } else { | ||||
|           _bw = 0x0A; | ||||
|       } | ||||
| 
 | ||||
|       setModulationParams(_sf, _bw, _cr); | ||||
| 
 | ||||
|   handleLowDataRate(); | ||||
|   optimizeModemSensitivity(); | ||||
| } | ||||
| 
 | ||||
| void sx128x::setCodingRate4(int denominator) | ||||
| { | ||||
|   if (denominator < 5) { | ||||
|     denominator = 5; | ||||
|   } else if (denominator > 8) { | ||||
|     denominator = 8; | ||||
|   } | ||||
| 
 | ||||
|   _cr = denominator - 4; | ||||
| 
 | ||||
|   // todo: add support for new interleaving scheme, see page 117 of sx1280
 | ||||
|   // datasheet
 | ||||
| 
 | ||||
|   // update cr values for sx1280's use
 | ||||
| 
 | ||||
|   setModulationParams(_sf, _bw, _cr); | ||||
| } | ||||
| 
 | ||||
| void sx128x::setPreambleLength(long length) | ||||
| { | ||||
|   _preambleLength = length; | ||||
|   setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx128x::setSyncWord(int sw) | ||||
| { | ||||
|     // not implemented
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::enableCrc() | ||||
| { | ||||
|       _crcMode = 0x20; | ||||
|       setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx128x::disableCrc() | ||||
| { | ||||
|     _crcMode = 0; | ||||
|     setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| byte sx128x::random() | ||||
| { | ||||
|     // todo: implement
 | ||||
| } | ||||
| 
 | ||||
| void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) | ||||
| { | ||||
|   _ss = ss; | ||||
|   _reset = reset; | ||||
|   _dio0 = dio0; | ||||
|   _busy = busy; | ||||
|   _rxen = rxen; | ||||
|   _txen = txen; | ||||
| } | ||||
| 
 | ||||
| void sx128x::setSPIFrequency(uint32_t frequency) | ||||
| { | ||||
|   _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); | ||||
| } | ||||
| 
 | ||||
| void sx128x::dumpRegisters(Stream& out) | ||||
| { | ||||
|   for (int i = 0; i < 128; i++) { | ||||
|     out.print("0x"); | ||||
|     out.print(i, HEX); | ||||
|     out.print(": 0x"); | ||||
|     out.println(readRegister(i), HEX); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void sx128x::explicitHeaderMode() | ||||
| { | ||||
|   _implicitHeaderMode = 0; | ||||
| 
 | ||||
|   setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| void sx128x::implicitHeaderMode() | ||||
| { | ||||
|     _implicitHeaderMode = 0x80; | ||||
|     setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void ISR_VECT sx128x::handleDio0Rise() | ||||
| { | ||||
|     uint8_t buf[2]; | ||||
| 
 | ||||
|     buf[0] = 0x00; | ||||
|     buf[1] = 0x00; | ||||
| 
 | ||||
|     executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); | ||||
| 
 | ||||
|     executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2); | ||||
| 
 | ||||
|     if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { | ||||
|         // received a packet
 | ||||
|         _packetIndex = 0; | ||||
| 
 | ||||
|         uint8_t rxbuf[2] = {0}; | ||||
|         executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); | ||||
|         _rxPacketLength = rxbuf[0]; | ||||
|         _fifo_rx_addr_ptr = rxbuf[1]; | ||||
|         readBuffer(_packet, _rxPacketLength); | ||||
| 
 | ||||
|         if (_onReceive) { | ||||
|             _onReceive(_rxPacketLength); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ISR_VECT sx128x::onDio0Rise() | ||||
| { | ||||
|   sx128x_modem.handleDio0Rise(); | ||||
| } | ||||
| 
 | ||||
| sx128x sx128x_modem; | ||||
							
								
								
									
										144
									
								
								sx128x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								sx128x.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | ||||
| // Copyright (c) Sandeep Mistry. All rights reserved.
 | ||||
| // Licensed under the MIT license.
 | ||||
| 
 | ||||
| // Modifications and additions copyright 2023 by Mark Qvist
 | ||||
| // Obviously still under the MIT license.
 | ||||
| 
 | ||||
| #ifndef SX128X_H | ||||
| #define SX128X_H | ||||
| 
 | ||||
| #include <Arduino.h> | ||||
| #include <SPI.h> | ||||
| #include "Modem.h" | ||||
| 
 | ||||
| #define LORA_DEFAULT_SS_PIN    10 | ||||
| #define LORA_DEFAULT_RESET_PIN 9 | ||||
| #define LORA_DEFAULT_DIO0_PIN  2 | ||||
| #define LORA_DEFAULT_RXEN_PIN  -1 | ||||
| #define LORA_DEFAULT_TXEN_PIN  -1 | ||||
| #define LORA_DEFAULT_BUSY_PIN  -1 | ||||
| 
 | ||||
| #define PA_OUTPUT_RFO_PIN      0 | ||||
| #define PA_OUTPUT_PA_BOOST_PIN 1 | ||||
| 
 | ||||
| #define RSSI_OFFSET 157 | ||||
| 
 | ||||
| class sx128x : public Stream { | ||||
| public: | ||||
|   sx128x(); | ||||
| 
 | ||||
|   int begin(unsigned long frequency); | ||||
|   void end(); | ||||
| 
 | ||||
|   int beginPacket(int implicitHeader = false); | ||||
|   int endPacket(); | ||||
| 
 | ||||
|   int parsePacket(int size = 0); | ||||
|   int packetRssi(); | ||||
|   int currentRssi(); | ||||
|   uint8_t packetRssiRaw(); | ||||
|   uint8_t currentRssiRaw(); | ||||
|   uint8_t packetSnrRaw(); | ||||
|   float packetSnr(); | ||||
|   long packetFrequencyError(); | ||||
| 
 | ||||
|   // from Print
 | ||||
|   virtual size_t write(uint8_t byte); | ||||
|   virtual size_t write(const uint8_t *buffer, size_t size); | ||||
| 
 | ||||
|   // from Stream
 | ||||
|   virtual int available(); | ||||
|   virtual int read(); | ||||
|   virtual int peek(); | ||||
|   virtual void flush(); | ||||
| 
 | ||||
|   void onReceive(void(*callback)(int)); | ||||
| 
 | ||||
|   void receive(int size = 0); | ||||
|   void idle(); | ||||
|   void sleep(); | ||||
| 
 | ||||
|   bool preInit(); | ||||
|   uint8_t getTxPower(); | ||||
|   void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); | ||||
|   uint32_t getFrequency(); | ||||
|   void setFrequency(unsigned long frequency); | ||||
|   void setSpreadingFactor(int sf); | ||||
|   long getSignalBandwidth(); | ||||
|   void setSignalBandwidth(long sbw); | ||||
|   void setCodingRate4(int denominator); | ||||
|   void setPreambleLength(long length); | ||||
|   void setSyncWord(int sw); | ||||
|   uint8_t modemStatus(); | ||||
|   void enableCrc(); | ||||
|   void disableCrc(); | ||||
|   void enableTCXO(); | ||||
|   void disableTCXO(); | ||||
| 
 | ||||
|   void txAntEnable(); | ||||
|   void rxAntEnable(); | ||||
|   void loraMode(); | ||||
|   void waitOnBusy(); | ||||
|   void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|   void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); | ||||
|   void writeBuffer(const uint8_t* buffer, size_t size); | ||||
|   void readBuffer(uint8_t* buffer, size_t size); | ||||
|   void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); | ||||
|   void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); | ||||
| 
 | ||||
|   // deprecated
 | ||||
|   void crc() { enableCrc(); } | ||||
|   void noCrc() { disableCrc(); } | ||||
| 
 | ||||
|   byte random(); | ||||
| 
 | ||||
|   void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN); | ||||
|   void setSPIFrequency(uint32_t frequency); | ||||
| 
 | ||||
|   void dumpRegisters(Stream& out); | ||||
| 
 | ||||
| private: | ||||
|   void explicitHeaderMode(); | ||||
|   void implicitHeaderMode(); | ||||
| 
 | ||||
|   void handleDio0Rise(); | ||||
| 
 | ||||
|   uint8_t readRegister(uint16_t address); | ||||
|   void writeRegister(uint16_t address, uint8_t value); | ||||
|   uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); | ||||
| 
 | ||||
|   static void onDio0Rise(); | ||||
| 
 | ||||
|   void handleLowDataRate(); | ||||
|   void optimizeModemSensitivity(); | ||||
| 
 | ||||
| private: | ||||
|   SPISettings _spiSettings; | ||||
|   int _ss; | ||||
|   int _reset; | ||||
|   int _dio0; | ||||
|   int _rxen; | ||||
|   int _txen; | ||||
|   int _busy; | ||||
|   int _modem; | ||||
|   unsigned long _frequency; | ||||
|   int _txp; | ||||
|   uint8_t _sf; | ||||
|   uint8_t _bw; | ||||
|   uint8_t _cr; | ||||
|   int _packetIndex; | ||||
|   uint32_t _preambleLength; | ||||
|   int _implicitHeaderMode; | ||||
|   int _payloadLength; | ||||
|   int _crcMode; | ||||
|   int _fifo_tx_addr_ptr; | ||||
|   int _fifo_rx_addr_ptr; | ||||
|   uint8_t _packet[256]; | ||||
|   bool _preinit_done; | ||||
|   int _rxPacketLength; | ||||
|   void (*_onReceive)(int); | ||||
| }; | ||||
| 
 | ||||
| extern sx128x sx128x_modem; | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user