diff --git a/examples/AX25/AX25_Frames/AX25_Frames.ino b/examples/AX25/AX25_Frames/AX25_Frames.ino index c6af0ad8..45cbba32 100644 --- a/examples/AX25/AX25_Frames/AX25_Frames.ino +++ b/examples/AX25/AX25_Frames/AX25_Frames.ino @@ -19,6 +19,9 @@ Frames shown in this example are not exhaustive; all possible AX.25 frames should be supported. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ */ // include the library diff --git a/examples/AX25/AX25_Transmit/AX25_Transmit.ino b/examples/AX25/AX25_Transmit/AX25_Transmit.ino index 0a462eff..6ab9c59e 100644 --- a/examples/AX25/AX25_Transmit/AX25_Transmit.ino +++ b/examples/AX25/AX25_Transmit/AX25_Transmit.ino @@ -12,6 +12,9 @@ - SX126x - nRF24 - Si443x/RFM2x + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ */ // include the library @@ -37,8 +40,8 @@ void setup() { // initialize SX1278 Serial.print(F("[SX1278] Initializing ... ")); // carrier frequency: 434.0 MHz - // bit rate: 1.2 kbps (1200 baud AFSK AX.25) - // frequency deviation: 0.5 kHz (1200 baud AFSK AX.25) + // bit rate: 1.2 kbps (1200 baud 2-FSK AX.25) + // frequency deviation: 0.5 kHz (1200 baud 2-FSK AX.25) int state = fsk.beginFSK(434.0, 1.2, 0.5); // when using one of the non-LoRa modules for AX.25 diff --git a/examples/AX25/AX25_Transmit_AFSK/AX25_Transmit_AFSK.ino b/examples/AX25/AX25_Transmit_AFSK/AX25_Transmit_AFSK.ino new file mode 100644 index 00000000..aa9d82f4 --- /dev/null +++ b/examples/AX25/AX25_Transmit_AFSK/AX25_Transmit_AFSK.ino @@ -0,0 +1,100 @@ +/* + RadioLib AX.25 Transmit AFSK Example + + This example sends AX.25 messages using + SX1278's FSK modem. The data is modulated + as AFSK at 1200 baud using Bell 202 tones. + + Other modules that can be used for AX.25 + with AFSK modulation: + - SX127x/RFM9x + - RF69 + - SX1231 + - CC1101 + - nRF24 + - Si443x/RFM2x + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1278 has the following connections: +// NSS pin: 10 +// DIO0 pin: 2 +// RESET pin: 9 +// DIO1 pin: 3 +SX1278 fsk = new Module(10, 2, 9, 3); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1278 fsk = RadioShield.ModuleA; + +// create AFSK client instance using the FSK module +// pin 5 is connected to SX1278 DIO2 +AFSKClient audio(&fsk, 5); + +// create AX.25 client instance using the AFSK instance +AX25Client ax25(&audio); + +void setup() { + Serial.begin(9600); + + // initialize SX1278 + Serial.print(F("[SX1278] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 125.0 kHz + // output power: 13 dBm + // current limit: 100 mA + int state = fsk.beginFSK(434.0); + + // when using one of the non-LoRa modules for AX.25 + // (RF69, CC1101,, Si4432 etc.), use the basic begin() method + // int state = fsk.begin(); + + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } + + // initialize AX.25 client + Serial.print(F("[AX.25] Initializing ... ")); + // source station callsign: "N7LEM" + // source station SSID: 0 + // preamble length: 8 bytes + state = ax25.begin("N7LEM"); + if(state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while(true); + } +} + +void loop() { + // send AX.25 unnumbered information frame + Serial.print(F("[AX.25] Sending UI frame ... ")); + // destination station callsign: "NJ7P" + // destination station SSID: 0 + int state = ax25.transmit("Hello World!", "NJ7P"); + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else { + // some error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + delay(1000); +} diff --git a/src/protocols/AX25/AX25.cpp b/src/protocols/AX25/AX25.cpp index 7c6a7933..1693cbe7 100644 --- a/src/protocols/AX25/AX25.cpp +++ b/src/protocols/AX25/AX25.cpp @@ -112,6 +112,12 @@ void AX25Frame::setSendSequence(uint8_t seqNumber) { AX25Client::AX25Client(PhysicalLayer* phy) { _phy = phy; + _audio = nullptr; +} + +AX25Client::AX25Client(AFSKClient* audio) { + _phy = audio->_phy; + _audio = audio; } int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preambleLen) { @@ -130,11 +136,14 @@ int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t prea // save preamble length _preambleLen = preambleLen; - // disable physical layer data shaping and set encoding to NRZ - int16_t state = _phy->setDataShaping(0.0); - RADIOLIB_ASSERT(state); + // set module frequency deviation to 0 if using FSK + int16_t state = ERR_NONE; + if(_audio == nullptr) { + state = _phy->setFrequencyDeviation(0); + RADIOLIB_ASSERT(state); - state = _phy->setEncoding(0); + state = _phy->setEncoding(0); + } return(state); } @@ -154,7 +163,7 @@ int16_t AX25Client::sendFrame(AX25Frame* frame) { if(strlen(frame->destCallsign) > AX25_MAX_CALLSIGN_LEN) { return(ERR_INVALID_CALLSIGN); } - + // check repeater configuration #ifndef RADIOLIB_STATIC_ONLY if(!(((frame->repeaterCallsigns == NULL) && (frame->repeaterSSIDs == NULL) && (frame->numRepeaters == 0)) || @@ -333,7 +342,32 @@ int16_t AX25Client::sendFrame(AX25Frame* frame) { } // transmit - int16_t state = _phy->transmit(stuffedFrameBuff, stuffedFrameBuffLen); + int16_t state = ERR_NONE; + if(_audio == nullptr) { + state = _phy->transmit(stuffedFrameBuff, stuffedFrameBuffLen); + } else { + _phy->transmitDirect(); + + // iterate over all bytes in the buffer + for(uint32_t i = 0; i < stuffedFrameBuffLen; i++) { + + // check each bit + for(uint16_t mask = 0x80; mask >= 0x01; mask >>= 1) { + uint32_t start = micros(); + if(stuffedFrameBuff[i] & mask) { + _audio->tone(AX25_AFSK_MARK, false); + } else { + _audio->tone(AX25_AFSK_SPACE, false); + } + while(micros() - start < 833) { + yield(); + } + } + + } + + _audio->noTone(); + } // deallocate memory #ifndef RADIOLIB_STATIC_ONLY diff --git a/src/protocols/AX25/AX25.h b/src/protocols/AX25/AX25.h index bfe322af..dc385dfa 100644 --- a/src/protocols/AX25/AX25.h +++ b/src/protocols/AX25/AX25.h @@ -3,6 +3,7 @@ #include "../../TypeDef.h" #include "../PhysicalLayer/PhysicalLayer.h" +#include "../AFSK/AFSK.h" // macros to access bits in byte array, from http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html #define SET_BIT_IN_ARRAY(A, k) ( A[(k/8)] |= (1 << (k%8)) ) @@ -69,6 +70,13 @@ #define AX25_PID_NO_LAYER_3 0xF0 #define AX25_PID_ESCAPE_CHARACTER 0xFF +// AFSK tones in Hz +#define AX25_AFSK_MARK 1200 +#define AX25_AFSK_SPACE 2200 + +// tone duration in us (for 1200 baud AFSK) +#define AX25_AFSK_TONE_DURATION 833 + /*! \class AX25Frame @@ -254,12 +262,19 @@ class AX25Frame { class AX25Client { public: /*! - \brief Default constructor. + \brief Constructor for 2-FSK mode. \param phy Pointer to the wireless module providing PhysicalLayer communication. */ AX25Client(PhysicalLayer* phy); + /*! + \brief Constructor for AFSK mode. + + \param audio Pointer to the AFSK instance providing audio. + */ + AX25Client(AFSKClient* audio); + // basic methods /*! @@ -301,6 +316,7 @@ class AX25Client { private: #endif PhysicalLayer* _phy; + AFSKClient* _audio; char _srcCallsign[AX25_MAX_CALLSIGN_LEN + 1]; uint8_t _srcSSID;