/* * Functions for using SX127x in FSK mode (mainly receive) * Copyright (C) 2019 Hansi Reiser, dl9rdz * * Partially based on the SX1278 libraray for managing Semtech modules * Copyright (C) 2015 Wireless Open Source * http://wirelessopensource.com * * SPDX-License-Identifier: LGPL-2.1+ */ #include "SX1278FSK.h" #include "SPI.h" #include #include SX1278FSK::SX1278FSK() { // Initialize class variables }; static SPISettings spiset = SPISettings(40000000L, MSBFIRST, SPI_MODE0); /* Function: Turns the module ON. Returns: 0 on success, 1 otherwise */ uint8_t SX1278FSK::ON() { uint8_t state = 2; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'ON'")); #endif // Powering the module pinMode(SX1278_SS, OUTPUT); digitalWrite(SX1278_SS, HIGH); //Configure the MISO, MOSI, CS, SPCR. SPI.begin(); //Set most significant bit first SPI.setBitOrder(MSBFIRST); //Divide the clock frequency SPI.setClockDivider(SPI_CLOCK_DIV2); //Set data mode SPI.setDataMode(SPI_MODE0); // Set Maximum Over Current Protection state = setMaxCurrent(0x1B); if( state == 0 ) { #if (SX1278FSK_debug_mode > 1) Serial.println(F("## Setting ON with maximum current supply ##")); Serial.println(); #endif } else { return 1; } // set FSK mode state = setFSK(); return state; } /* Function: Turn the module OFF. Returns: Nothing */ void SX1278FSK::OFF() { #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'OFF'")); #endif SPI.end(); // Powering the module pinMode(SX1278_SS,OUTPUT); digitalWrite(SX1278_SS,LOW); #if (SX1278FSK_debug_mode > 1) Serial.println(F("## Setting OFF ##")); Serial.println(); #endif } /* Function: Reads the indicated register. Returns: The content of the register Parameters: address: address register to read from */ byte SX1278FSK::readRegister(byte address) { byte value = 0x00; SPI.beginTransaction(spiset); digitalWrite(SX1278_SS,LOW); //delay(1); bitClear(address, 7); // Bit 7 cleared to write in registers SPI.transfer(address); value = SPI.transfer(0x00); digitalWrite(SX1278_SS,HIGH); SPI.endTransaction(); #if (SX1278FSK_debug_mode > 1) if(address!=0x3F) { Serial.print(F("## Reading: ##\t")); Serial.print(F("Register ")); Serial.print(address, HEX); Serial.print(F(": ")); Serial.print(value, HEX); Serial.println(); } #endif return value; } /* Function: Writes on the indicated register. Returns: Nothing Parameters: address: address register to write in data: value to write in the register */ void SX1278FSK::writeRegister(byte address, byte data) { SPI.beginTransaction(spiset); digitalWrite(SX1278_SS,LOW); //delay(1); bitSet(address, 7); // Bit 7 set to read from registers SPI.transfer(address); SPI.transfer(data); digitalWrite(SX1278_SS,HIGH); SPI.endTransaction(); #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Writing: ##\t")); Serial.print(F("Register ")); bitClear(address, 7); Serial.print(address, HEX); Serial.print(F(": ")); Serial.print(data, HEX); Serial.println(); #endif } /* * Function: Clears the IRQ flags * * Configuration registers are accessed through the SPI interface. * Registers are readable in all device mode including Sleep. However, they * should be written only in Sleep and Stand-by modes. * * Returns: Nothing */ void SX1278FSK::clearIRQFlags() { byte st0; // Save the previous status st0 = readRegister(REG_OP_MODE); // Stdby mode to write in registers writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // FSK mode flags1 register writeRegister(REG_IRQ_FLAGS1, 0xFF); // FSK mode flags2 register writeRegister(REG_IRQ_FLAGS2, 0xFF); // Getting back to previous status if(st0 != FSK_STANDBY_MODE) { writeRegister(REG_OP_MODE, st0); } #if (SX1278FSK_debug_mode > 1) Serial.println(F("## FSK flags cleared ##")); #endif } /* Function: Sets the module in FSK mode. Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors */ uint8_t SX1278FSK::setFSK() { uint8_t state = 2; byte st0; byte config1; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'setFSK'")); #endif writeRegister(REG_OP_MODE, FSK_SLEEP_MODE); // Sleep mode (mandatory to change mode) // If we are in LORA mode, above line activate Sleep mode, but does not change mode to FSK // as mode change is only allowed in sleep mode. Next line changes to FSK writeRegister(REG_OP_MODE, FSK_SLEEP_MODE); writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // FSK standby mode //writeRegister(REG_FIFO_THRESH, 0x80); // condition to start packet tx //config1 = readRegister(REG_SYNC_CONFIG); //config1 = config1 & B00111111; //writeRegister(REG_SYNC_CONFIG,config1); delay(100); st0 = readRegister(REG_OP_MODE); // Reading config mode if( st0 == FSK_STANDBY_MODE ) { // FSK mode state = 0; #if (SX1278FSK_debug_mode > 1) Serial.println(F("## FSK set with success ##")); Serial.println(); #endif } else { // LoRa mode state = 1; Serial.println( st0 ); #if (SX1278FSK_debug_mode > 1) Serial.println(F("** There has been an error while setting FSK **")); Serial.println(); #endif } return state; } /* Function: Sets FSK bitrate * Returns: 0 for success, >0 in case of error * Parameters: bps: requested bitrate * (raw data rate, for Mancester encoding, the effective bitrate is bps/2) */ uint8_t SX1278FSK::setBitrate(float bps) { // TODO: Check if FSK mode is active // check if bitrate is allowed allowed bitrate if((bps < 1200) || (bps > 300000)) { return 1; } // set mode to FSK STANDBY writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // set bit rate uint16_t bitRate = (SX127X_CRYSTAL_FREQ * 1.0) / bps; writeRegister(REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8); writeRegister(REG_BITRATE_LSB, (bitRate & 0x00FF)); // also set fractional part uint16_t fracRate = (SX127X_CRYSTAL_FREQ * 16.0) / bps - bitRate * 16 + 0.5; writeRegister(REG_BIT_RATE_FRAC, fracRate&0x0F); return 0; } /* Function: Gets configured bitrate * Returns bitrate in bit/second */ float SX1278FSK::getBitrate() { uint8_t fmsb = readRegister(REG_BITRATE_MSB); uint8_t flsb = readRegister(REG_BITRATE_LSB); uint8_t ffrac = readRegister(REG_BIT_RATE_FRAC) & 0x0F; return SX127X_CRYSTAL_FREQ / ( (fmsb<<8) + flsb + ffrac / 16.0 ); } //typedef struct rxbwset { float bw; uint8_t mant; uint8_t rxp; } st_rxbwsettings; uint8_t SX1278FSK::setRxBandwidth(float bw) { // TODO: Check if in FSK mode // if(bw<2600 || bw>250000) { return 1; /* invalid */ } uint8_t rxbwexp = 1; bw = SX127X_CRYSTAL_FREQ / bw / 8; while(bw>31) { rxbwexp++; bw/=2.0; } uint8_t rxbwmant = bw<17?0 : bw<21? 1:2; // set mode to FSK STANDBY writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); writeRegister(REG_RX_BW, rxbwexp | (rxbwmant<<3)); return 0; } float SX1278FSK::getRxBandwidth() { uint8_t rxbw = readRegister(REG_RX_BW); uint8_t rxbwexp = rxbw&0x07; uint8_t rxbwmant = (rxbw>>3)&0x03; rxbwmant = 16 + 4*rxbwmant; return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2)); } uint8_t SX1278FSK::setAFCBandwidth(float bw) { // TODO: Check if in FSK mode // if(bw<2600 || bw>250000) { return 1; /* invalid */ } uint8_t rxbwexp = 1; bw = SX127X_CRYSTAL_FREQ / bw / 8; while(bw>31) { rxbwexp++; bw/=2.0; } uint8_t rxbwmant = bw<17?0 : bw<21? 1:2; // set mode to FSK STANDBY writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); writeRegister(REG_AFC_BW, rxbwexp | (rxbwmant<<3)); return 0; } float SX1278FSK::getAFCBandwidth() { uint8_t rxbw = readRegister(REG_AFC_BW); uint8_t rxbwexp = rxbw&0x07; uint8_t rxbwmant = (rxbw>>3)&0x03; rxbwmant = 16 + 4*rxbwmant; return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2)); } uint8_t SX1278FSK::setFrequency(float freq) { // set mode to FSK STANDBY writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); freq += sonde.config.freqofs; // manual frequency correction uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ; writeRegister(REG_FRF_MSB, (frf&0xff0000)>>16); writeRegister(REG_FRF_MID, (frf&0x00ff00)>>8); writeRegister(REG_FRF_LSB, (frf&0x0000ff)); return 0; } float SX1278FSK::getFrequency() { uint8_t fmsb = readRegister(REG_FRF_MSB); uint8_t fmid = readRegister(REG_FRF_MID); uint8_t flsb = readRegister(REG_FRF_LSB); return ((fmsb<<16)|(fmid<<8)|flsb) * 1.0 / (1<<19) * SX127X_CRYSTAL_FREQ; } static int gaintab[]={-999,0,-6,-12,-24,-36,-48,-999}; int SX1278FSK::getLNAGain() { int gain = (readRegister(REG_LNA)>>5)&0x07; return gaintab[gain]; } uint8_t SX1278FSK::setLNAGain(int gain) { uint8_t g=1; while(gain8) return 1; for(int i=0; i 1) Serial.println(); Serial.println(F("Starting 'getPreambleLength'")); #endif p_length = readRegister(REG_PREAMBLE_MSB_FSK); p_length = (p_length<<8) | readRegister(REG_PREAMBLE_LSB_FSK); #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Preamble length configured is ")); Serial.print(p_length, HEX); Serial.print(F(" ##")); Serial.println(); #endif return p_length; } /* Function: Sets the preamble length in the module Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors Parameters: l: length value to set as preamble length. */ uint8_t SX1278FSK::setPreambleLength(uint16_t l) { byte st0; int8_t state = 2; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'setPreambleLength'")); #endif st0 = readRegister(REG_OP_MODE); // Save the previous status writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set Standby mode to write in registers // Storing MSB preamble length in FSK mode writeRegister(REG_PREAMBLE_MSB_FSK, l>>8); writeRegister(REG_PREAMBLE_LSB_FSK, l&0xFF); state = 0; #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Preamble length ")); Serial.print(l, HEX); Serial.println(F(" has been successfully set ##")); Serial.println(); #endif if(st0 != FSK_STANDBY_MODE) { writeRegister(REG_OP_MODE, st0); // Getting back to previous status } return state; } /* Function: Gets the payload length from the module. Returns: configured length; -1 on error */ int SX1278FSK::getPayloadLength() { int length; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'getPayloadLength'")); #endif length = readRegister(REG_PAYLOAD_LENGTH_FSK); #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Payload length configured is ")); Serial.print(length); Serial.println(F(" ##")); #endif return length; } /* Function: Sets the payload length from the module. Returns: 0 for ok, otherwise error // TODO: Larger than 255 bytes? */ uint8_t SX1278FSK::setPayloadLength(int len) { #if (SX1278FSK_debug_mode > 1) Serial.print(F("Starting 'setPayloadLength'")); Serial.println(len); #endif uint8_t conf2 = readRegister(REG_PACKET_CONFIG2); conf2 = (conf2 & 0xF8) | ( (len>>8)&0x7 ); writeRegister(REG_PACKET_CONFIG2, conf2); writeRegister(REG_PAYLOAD_LENGTH_FSK, len&0xFF); return 0; } /* Function: Gets the current value of RSSI. Returns: RSSI value */ int16_t SX1278FSK::getRSSI() { int16_t RSSI; //int rssi_mean = 0; int total = 1; /// FSK mode // get mean value of RSSI for(int i = 0; i < total; i++) { RSSI = readRegister(REG_RSSI_VALUE_FSK); //rssi_mean += _RSSI; } //rssi_mean = rssi_mean / total; //RSSI = rssi_mean; #if (SX1278FSK_debug_mode > 0) Serial.print(F("## RSSI value is ")); Serial.print(RSSI); Serial.println(F(" ##")); #endif return RSSI; } /* Function: Gets the current value of FEI (frequency error indication) Returns: FEI value in Hz */ int32_t SX1278FSK::getFEI() { int32_t FEI; int16_t regval = (readRegister(REG_FEI_MSB)<<8) | readRegister(REG_FEI_LSB); //Serial.printf("feireg: %04x\n", regval); FEI = (int32_t)(regval * SX127X_FSTEP); return FEI; } /* Function: Gets the current value of AFC (automated frequency correction) Returns: AFC value in Hz */ int32_t SX1278FSK::getAFC() { int32_t AFC; int16_t regval = (readRegister(REG_AFC_MSB)<<8) | readRegister(REG_AFC_LSB); //Serial.printf("afcreg: %04x\n", regval); AFC = (int32_t)(regval * SX127X_FSTEP); return AFC; } /* Function: Gets the current supply limit of the power amplifier, protecting battery chemistries. Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors Parameters: rate: value to compute the maximum current supply. Maximum current is 45+5*'rate' [mA] */ int SX1278FSK::getMaxCurrent() { int value; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'getMaxCurrent'")); #endif value = readRegister(REG_OCP); // extract only the OcpTrim value from the OCP register value &= B00011111; if( value <= 15 ) { value = (45 + (5 * value)); } else if( value <= 27 ) { value = (-30 + (10 * value)); } else { value = 240; } #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Maximum current supply configured is ")); Serial.print(value, DEC); Serial.println(F(" mA ##")); Serial.println(); #endif return value; } /* Function: Limits the current supply of the power amplifier, protecting battery chemistries. Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors state = -1 --> Forbidden parameter value for this function Parameters: rate: value to compute the maximum current supply. Range: 0x00 to 0x1B. The Maximum current is: Imax = 45+5*OcpTrim [mA] if OcpTrim <= 15 (120 mA) / Imax = -30+10*OcpTrim [mA] if 15 < OcpTrim <= 27 (130 to 240 mA) Imax = 240mA for higher settings */ int8_t SX1278FSK::setMaxCurrent(uint8_t rate) { int8_t state = 2; byte st0; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'setMaxCurrent'")); #endif // Maximum rate value = 0x1B, because maximum current supply = 240 mA if (rate > 0x1B) { state = -1; #if (SX1278FSK_debug_mode > 1) Serial.print(F("** Maximum current supply is 240 mA, ")); Serial.println(F("so maximum parameter value must be 27 (DEC) or 0x1B (HEX) **")); Serial.println(); #endif } else { // Enable Over Current Protection rate |= B00100000; state = 1; st0 = readRegister(REG_OP_MODE); // Save the previous status writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set FSK Standby mode to write in registers writeRegister(REG_OCP, rate); // Modifying maximum current supply if(st0 != FSK_STANDBY_MODE) { writeRegister(REG_OP_MODE, st0); // Getting back to previous status } state = 0; } return state; } /* Function: Configures the module to receive information. Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors */ uint8_t SX1278FSK::receive() { uint8_t state = 1; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'receive'")); #endif // TODO: Is there anything else to be done? // writeRegister(REG_OP_MODE, FSK_RX_MODE); state = 0; #if (SX1278FSK_debug_mode > 1) Serial.println(F("## Receiving FSK mode activated with success ##")); #endif return state; } /* Function: Configures the module to receive a packet Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors Parameters: wait: timeout in ms data: memory where to place received data */ uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data) { int di=0; uint8_t state = 2; unsigned long previous; byte value = 0x00; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'receivePacketTimeout'")); #endif // set RX mode state = receive(); if(state != 0) { return state; } #if (SX1278FSK_debug_mode > 0) Serial.println(F("RX mode sucessfully activated")); #endif previous = millis(); /// FSK mode value = readRegister(REG_IRQ_FLAGS2); byte ready=0; // while not yet done or FIFO not yet empty while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) ) { if( bitRead(value,2)==1 ) ready=1; if( bitRead(value, 6) == 0 ) { // FIFO not empty data[di++] = readRegister(REG_FIFO); // It's a bit of a hack.... get RSSI and AFC (a) at beginning of packet and // for RS41 after about 0.5 sec. It might be more logical to put this decoder-specific // code into RS41.cpp instead of this file... (maybe TODO?) if(di==1 || di==290 ) { int rssi=getRSSI(); int afc=getAFC(); #if 0 Serial.printf("Test(%d): RSSI=%d", rxtask.currentSonde, rssi/2); Serial.print("Test: AFC="); Serial.println(afc); #endif sonde.sondeList[rxtask.currentSonde].rssi = rssi; sonde.sondeList[rxtask.currentSonde].afc = afc; if(rxtask.receiveResult==0xFFFF) rxtask.receiveResult = RX_UPDATERSSI; //sonde.si()->rssi = rssi; //sonde.si()->afc = afc; } if(di>520) { // TODO Serial.println("TOO MUCH DATA"); break; } previous = millis(); // reset timeout after receiving data } else { delay(10); } value = readRegister(REG_IRQ_FLAGS2); } if( !ready || bitRead(value, 6)==0) { #if 1&&(SX1278FSK_debug_mode > 0) Serial.println(F("** The timeout has expired **")); Serial.println(); #endif sonde.si()->rssi = getRSSI(); writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Setting standby FSK mode return 1; // TIMEOUT } #if (SX1278FSK_debug_mode > 0) Serial.println(F("## Packet received:")); for(unsigned int i = 0; i < di; i++) { Serial.print(data[i], HEX); // Printing payload Serial.print("|"); } Serial.println(F(" ##")); #endif state = 0; // Initializing flags clearIRQFlags(); return state; } #if 0 /* Function: It gets the temperature from the measurement block module. Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors */ uint8_t SX1278FSK::getTemp() { byte st0; uint8_t state = 2; #if (SX1278FSK_debug_mode > 1) Serial.println(); Serial.println(F("Starting 'getTemp'")); #endif st0 = readRegister(REG_OP_MODE); // Save the previous status if( _modem == LORA ) { // Allowing access to FSK registers while in LoRa standby mode writeRegister(REG_OP_MODE, LORA_STANDBY_FSK_REGS_MODE); } state = 1; // Saving temperature value _temp = readRegister(REG_TEMP); if( _temp & 0x80 ) // The SNR sign bit is 1 { // Invert and divide by 4 _temp = ( ( ~_temp + 1 ) & 0xFF ); } else { // Divide by 4 _temp = ( _temp & 0xFF ); } #if (SX1278FSK_debug_mode > 1) Serial.print(F("## Temperature is: ")); Serial.print(_temp); Serial.println(F(" ##")); Serial.println(); #endif if( _modem == LORA ) { writeRegister(REG_OP_MODE, st0); // Getting back to previous status } state = 0; return state; } /* Function: It prints the registers related to RX Returns: Integer that determines if there has been any error state = 2 --> The command has not been executed state = 1 --> There has been an error while executing the command state = 0 --> The command has been executed with no errors */ void SX1278FSK::showRxRegisters() { Serial.println(F("\n--- Show RX register ---")); // variable byte reg; for(int i = 0x00; i < 0x80; i++) { reg = readRegister(i); Serial.print(F("Reg 0x")); Serial.print(i, HEX); Serial.print(F(":")); Serial.print(reg, HEX); Serial.println(); delay(100); } Serial.println(F("------------------------")); } #endif SX1278FSK sx1278 = SX1278FSK();