diff --git a/examples/AFSK/AFSK_No_Tone/AFSK_No_Tone.ino b/examples/AFSK/AFSK_No_Tone/AFSK_No_Tone.ino
new file mode 100644
index 00000000..1635455e
--- /dev/null
+++ b/examples/AFSK/AFSK_No_Tone/AFSK_No_Tone.ino
@@ -0,0 +1,87 @@
+/*
+   RadioLib AFSK Example
+
+   This example shows hot to send audio FSK tones
+   using SX1278's FSK modem, on platforms that do
+   not support the tone() function.
+
+   Other modules that can be used for AFSK:
+    - SX127x/RFM9x
+    - RF69
+    - SX1231
+    - CC1101
+    - Si443x/RFM2x
+
+   For default module settings, see the wiki page
+   https://github.com/jgromes/RadioLib/wiki/Default-configuration
+
+   For full API reference, see the GitHub Pages
+   https://jgromes.github.io/RadioLib/
+*/
+
+// include the library
+#include <RadioLib.h>
+
+// SX1278 has the following connections:
+// NSS pin:   10
+// DIO0 pin:  2
+// RESET pin: 9
+// DIO1 pin:  3
+SX1278 radio = new Module(5, 2, 9, 3);
+
+// create AFSK client instance using the FSK module
+// enable tone emulation by setting the pin to
+// "not connected"
+AFSKClient audio(&radio, RADIOLIB_NC);
+
+void setup() {
+  Serial.begin(9600);
+
+  // initialize SX1278 with default settings
+  Serial.print(F("[SX1278] Initializing ... "));
+  int state = radio.beginFSK();
+
+  // when using one of the non-LoRa modules for AFSK
+  // (RF69, CC1101, Si4432 etc.), use the basic begin() method
+  // int state = radio.begin();
+
+  if(state == RADIOLIB_ERR_NONE) {
+    Serial.println(F("success!"));
+  } else {
+    Serial.print(F("failed, code "));
+    Serial.println(state);
+    while(true);
+  }
+
+  // initialize AFSK client
+  Serial.print(F("[AFSK] Initializing ... "));
+  state = audio.begin();
+  if(state == RADIOLIB_ERR_NONE) {
+    Serial.println(F("success!"));
+  } else {
+    Serial.print(F("failed, code "));
+    Serial.println(state);
+    while(true);
+  }
+}
+
+void loop() {
+  // AFSKClient can be used to transmit tones,
+  // same as Arduino tone() function
+  
+  // 400 Hz tone
+  Serial.print(F("[AFSK] 400 Hz tone ... "));
+  const size_t len = 64;
+  uint8_t bits[len] = { 0 };
+  for(size_t i = 0; i < len; i++) {
+    bits[i] = 0xF0;
+  }
+  audio.emulateTone(400, bits, len);
+  Serial.println(F("done!"));
+  delay(1000);
+
+  // AFSKClient can also be used to transmit HAM-friendly
+  // RTTY, Morse code, Hellschreiber, SSTV and AX.25.
+  // Details on how to use AFSK are in the example
+  // folders for each of the above modes.
+}
diff --git a/src/protocols/AFSK/AFSK.cpp b/src/protocols/AFSK/AFSK.cpp
index 29c00448..b44b8057 100644
--- a/src/protocols/AFSK/AFSK.cpp
+++ b/src/protocols/AFSK/AFSK.cpp
@@ -6,6 +6,19 @@ AFSKClient::AFSKClient(PhysicalLayer* phy, RADIOLIB_PIN_TYPE pin): _pin(pin) {
 }
 
 int16_t AFSKClient::begin() {
+  if(_pin == RADIOLIB_NC) {
+    // set encoding to NRZ
+    int16_t state = _phy->setEncoding(RADIOLIB_ENCODING_NRZ);
+    RADIOLIB_ASSERT(state);
+
+    // disable preamble
+    state = _phy->setPreambleLength(0);
+    RADIOLIB_ASSERT(state);
+
+    // disable CRC
+    state = _phy->setCrcFiltering(false);
+    return(state);
+  }
   return(_phy->startDirect());
 }
 
@@ -34,4 +47,29 @@ int16_t AFSKClient::noTone(bool keepOn) {
   return(_phy->standby());
 }
 
+int16_t AFSKClient::tones(float baseFreq, uint8_t* bits, size_t len) {
+  // set fixed packet length mode
+  int16_t state = _phy->fixedPacketLengthMode(len);
+  RADIOLIB_ASSERT(state);
+
+  float br = baseFreq;
+  if((br < 1200)) {
+    if(br < 1200/8) {
+      // this is too low to achieve by a single octet
+      // FIXME - if tones lower than 150 Hz are needed, multiple octets must be used
+      return(RADIOLIB_ERR_INVALID_FREQUENCY);
+    }
+    // this tone frequency is lower than what most modules can do
+    // but we can easily emulate tones in this range
+    br *= 8.0;
+  }
+
+  // set the base frequency
+  state = _phy->setBitRate(br / 1000.0);
+  RADIOLIB_ASSERT(state);
+
+  // transmit
+  return(_phy->transmit(bits, len));
+}
+
 #endif
diff --git a/src/protocols/AFSK/AFSK.h b/src/protocols/AFSK/AFSK.h
index 72b45471..ce651d63 100644
--- a/src/protocols/AFSK/AFSK.h
+++ b/src/protocols/AFSK/AFSK.h
@@ -52,6 +52,8 @@ class AFSKClient  {
     */
     int16_t noTone(bool keepOn = false);
 
+    int16_t emulateTone(float baseFreq, uint8_t* bits, size_t len);
+
 #if !defined(RADIOLIB_GODMODE)
   private:
 #endif
diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.h b/src/protocols/PhysicalLayer/PhysicalLayer.h
index 425dddc7..97245e22 100644
--- a/src/protocols/PhysicalLayer/PhysicalLayer.h
+++ b/src/protocols/PhysicalLayer/PhysicalLayer.h
@@ -302,6 +302,11 @@ class PhysicalLayer {
 
     virtual Module* getMod() = 0;
 
+    virtual int16_t setPreambleLength(uint16_t preambleLength) = 0;
+    virtual int16_t setCrcFiltering(bool crcOn) = 0;
+    virtual int16_t fixedPacketLengthMode(uint8_t preambleLength) = 0;
+    virtual int16_t setBitRate(float br) = 0;
+
   protected:
     void updateDirectBuffer(uint8_t bit);