diff --git a/.gitignore b/.gitignore
index 659c845b..b917a7ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -109,5 +109,5 @@ $RECYCLE.BIN/
 *.dSYM
 
 # cmake
-build/
+build*/
 tmp/
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c01045b..ce92decb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -320,6 +320,14 @@ else()
   set(HAMLIB_LIBRARIES "")
 endif()
 
+find_package(gpiod)
+if(GPIOD_FOUND)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_GPIOD")
+else()
+  set(GPIOD_INCLUDE_DIRS "")
+  set(GPIOD_LIBRARIES "")
+endif()
+
 if(LINUX)
   find_package(ALSA REQUIRED)
   if(ALSA_FOUND)
diff --git a/cmake/modules/FindCompiler.cmake b/cmake/modules/FindCompiler.cmake
index fe036e4b..91e1b89c 100644
--- a/cmake/modules/FindCompiler.cmake
+++ b/cmake/modules/FindCompiler.cmake
@@ -5,9 +5,9 @@ elseif(NOT DEFINED C_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
   set(C_GCC 1)
 elseif(NOT DEFINED C_MSVC AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
   set(C_MSVC 1)
-  if(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS_EQUAL 1929)
+  if(MSVC_VERSION GREATER 1919 AND MSVC_VERSION LESS 1926)
     set(VS2019 ON)
-  elseif(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS_EQUAL 1919)
+  elseif(MSVC_VERSION GREATER 1910 AND MSVC_VERSION LESS 1919)
     set(VS2017 ON)
   elseif(MSVC_VERSION GREATER 1899 AND MSVC_VERSION LESS 1910)
     set(VS2015 ON)
diff --git a/cmake/modules/Findgpiod.cmake b/cmake/modules/Findgpiod.cmake
new file mode 100644
index 00000000..bf5be305
--- /dev/null
+++ b/cmake/modules/Findgpiod.cmake
@@ -0,0 +1,23 @@
+# - Try to find libgpiod
+# Once done this will define
+#  GPIOD_FOUND - System has libgpiod
+#  GPIOD_INCLUDE_DIRS - The libgpiod include directories
+#  GPIOD_LIBRARIES - The libraries needed to use libgpiod
+#  GPIOD_DEFINITIONS - Compiler switches required for using libgpiod
+
+find_package(PkgConfig)
+pkg_check_modules(PC_GPIOD QUIET gpiod)
+
+find_path(GPIOD_INCLUDE_DIR gpiod.h)
+find_library(GPIOD_LIBRARY NAMES gpiod)
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set GPIOD_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(gpiod  DEFAULT_MSG
+                                  GPIOD_LIBRARY GPIOD_INCLUDE_DIR)
+
+mark_as_advanced(GPIOD_INCLUDE_DIR GPIOD_LIBRARY)
+
+set(GPIOD_LIBRARIES ${GPIOD_LIBRARY})
+set(GPIOD_INCLUDE_DIRS ${GPIOD_INCLUDE_DIR})
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a2c3963d..5320a163 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,6 +10,7 @@ include_directories(
   ${PORTAUDIO_INCLUDE_DIRS}
   ${SNDIO_INCLUDE_DIRS}
   ${CUSTOM_GEOTRANZ_DIR}
+  ${GPIOD_INCLUDE_DIRS}
   ${CUSTOM_HIDAPI_DIR}
   )
 
@@ -155,6 +156,7 @@ target_link_libraries(direwolf
   ${ALSA_LIBRARIES}
   ${UDEV_LIBRARIES}
   ${PORTAUDIO_LIBRARIES}
+  ${GPIOD_LIBRARIES}
   ${SNDIO_LIBRARIES}
   ${AVAHI_LIBRARIES}
   )
diff --git a/src/audio.h b/src/audio.h
index cb5ca94e..4fc05708 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -28,7 +28,8 @@
 enum ptt_method_e { 
 	PTT_METHOD_NONE,	/* VOX or no transmit. */
 	PTT_METHOD_SERIAL,	/* Serial port RTS or DTR. */
-	PTT_METHOD_GPIO,	/* General purpose I/O, Linux only. */
+	PTT_METHOD_GPIO,	/* General purpose I/O using sysfs, deprecated after 2020, Linux only. */
+	PTT_METHOD_GPIOD,	/* General purpose I/O, using libgpiod, Linux only. */
 	PTT_METHOD_LPT,	    	/* Parallel printer port, Linux only. */
 	PTT_METHOD_HAMLIB, 	/* HAMLib, Linux only. */
 	PTT_METHOD_CM108 };	/* GPIO pin of CM108/CM119/etc.  Linux only. */
@@ -74,16 +75,23 @@ struct audio_s {
 
 	    /* Properties of the sound device. */
 
-	    int defined;		/* Was device defined? */
-					/* First one defaults to yes. */
+	    int defined;		/* Was device defined?   0=no.  >0 for yes.  */
+					/* First channel defaults to 2 for yes with default config. */
+					/* 1 means it was defined by user. */
+
+	    int copy_from;		/* >=0  means copy contents from another audio device. */
+					/* In this case we don't have device names, below. */
+					/* Num channels, samples/sec, and bit/sample are copied from */
+					/* original device and can't be changed. */
+					/* -1 for normal case. */
 
 	    char adevice_in[80];	/* Name of the audio input device (or file?). */
-					/* TODO: Can be "-" to read from stdin. */
+					/* Can be udp:nnn for UDP or "-" to read from stdin. */
 
 	    char adevice_out[80];	/* Name of the audio output device (or file?). */
 
 	    int num_channels;		/* Should be 1 for mono or 2 for stereo. */
-	    int samples_per_sec;	/* Audio sampling rate.  Typically 11025, 22050, or 44100. */
+	    int samples_per_sec;	/* Audio sampling rate.  Typically 11025, 22050, 44100, or 48000. */
 	    int bits_per_sample;	/* 8 (unsigned char) or 16 (signed short). */
 
 	} adev[MAX_ADEVS];
@@ -304,6 +312,7 @@ struct audio_s {
 					/* the case for CubieBoard where it was longer. */
 					/* This is filled in by ptt_init so we don't have to */
 					/* recalculate it each time we access it. */
+					/* Also GPIO chip name for GPIOD method. Looks like 'gpiochip4' */
 
 					/* This could probably be collapsed into ptt_device instead of being separate. */
 
diff --git a/src/config.c b/src/config.c
index 739eb2f6..747d0e60 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1842,6 +1842,45 @@ void config_init (char *fname, struct audio_s *p_audio_config,
 	      }
 	      p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIO;
 #endif
+	    }
+	    else if (strcasecmp(t, "GPIOD") == 0) {
+#if __WIN32__
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Config file line %d: %s with GPIOD is only available on Linux.\n", line, otname);
+#else		
+#if defined(USE_GPIOD)
+	      t = split(NULL,0);
+	      if (t == NULL) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Config file line %d: Missing GPIO chip name for %s.\n", line, otname);
+	        dw_printf ("Use the \"gpioinfo\" command to get a list of gpio chip names and corresponding I/O lines.\n");
+	        continue;
+	      }
+	      strlcpy(p_audio_config->achan[channel].octrl[ot].out_gpio_name, t, 
+	              sizeof(p_audio_config->achan[channel].octrl[ot].out_gpio_name));
+
+	      t = split(NULL,0);
+	      if (t == NULL) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf("Config file line %d: Missing GPIO number for %s.\n", line, otname);
+	        continue;
+	      }
+
+	      if (*t == '-') {
+	        p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t+1);
+		p_audio_config->achan[channel].octrl[ot].ptt_invert = 1;
+	      }
+	      else {
+	        p_audio_config->achan[channel].octrl[ot].out_gpio_num = atoi(t);
+		p_audio_config->achan[channel].octrl[ot].ptt_invert = 0;
+	      }
+	      p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_GPIOD;
+#else
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Application was not built with optional support for GPIOD.\n");
+	      dw_printf ("Install packages gpiod and libgpiod-dev, remove 'build' subdirectory, then rebuild.\n");
+#endif /* USE_GPIOD*/
+#endif /* __WIN32__ */
 	    }
 	    else if (strcasecmp(t, "LPT") == 0) {
 
diff --git a/src/direwolf.c b/src/direwolf.c
index c6ed9d4d..c8bb3a1b 100644
--- a/src/direwolf.c
+++ b/src/direwolf.c
@@ -306,7 +306,7 @@ int main (int argc, char *argv[])
 	//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
 
 
-#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD
+#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD || USE_GPIOD
 	dw_printf ("Includes optional support for: ");
  #if defined(ENABLE_GPSD)
 	dw_printf (" gpsd");
@@ -317,6 +317,9 @@ int main (int argc, char *argv[])
  #if defined(USE_CM108)
 	dw_printf (" cm108-ptt");
  #endif
+ #if defined(USE_GPIOD)
+	dw_printf (" libgpiod");
+ #endif
  #if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD)
 	dw_printf (" dns-sd");
  #endif
diff --git a/src/dlq.c b/src/dlq.c
index f56b8649..aa25b84f 100644
--- a/src/dlq.c
+++ b/src/dlq.c
@@ -60,6 +60,8 @@
 /* The queue is a linked list of these. */
 
 static struct dlq_item_s *queue_head = NULL;	/* Head of linked list for queue. */
+static struct dlq_item_s *queue_tail = NULL;	/* Tail of linked list for queue. */
+int queue_length = 0;	/* Count of items in queue */
 
 #if __WIN32__
 
@@ -75,8 +77,6 @@ static pthread_mutex_t dlq_mutex;		/* Critical section for updating queues. */
 
 static pthread_cond_t wake_up_cond;		/* Notify received packet processing thread when queue not empty. */
 
-static pthread_mutex_t wake_up_mutex;		/* Required by cond_wait. */
-
 static volatile int recv_thread_is_waiting = 0;
 
 #endif
@@ -117,7 +117,8 @@ void dlq_init (void)
 	dw_printf ("dlq_init ( )\n");
 #endif
 
-	queue_head = NULL;
+	queue_head = queue_tail = NULL;
+	queue_length = 0;
 
 
 #if DEBUG
@@ -129,13 +130,6 @@ void dlq_init (void)
 	InitializeCriticalSection (&dlq_cs);
 #else
 	int err;
-	err = pthread_mutex_init (&wake_up_mutex, NULL);
-	if (err != 0) {
-	  text_color_set(DW_COLOR_ERROR);
-	  dw_printf ("dlq_init: pthread_mutex_init err=%d", err);
-	  perror ("");
-	  exit (EXIT_FAILURE);
-	}
 	err = pthread_mutex_init (&dlq_mutex, NULL);
 	if (err != 0) {
 	  text_color_set(DW_COLOR_ERROR);
@@ -314,9 +308,6 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
 
 static void append_to_queue (struct dlq_item_s *pnew)
 {
-	struct dlq_item_s *plast;
-	int queue_length = 0;
-
 	if ( ! was_init) {
 	  dlq_init ();
 	}
@@ -341,30 +332,19 @@ static void append_to_queue (struct dlq_item_s *pnew)
 #endif
 
 	if (queue_head == NULL) {
-	  queue_head = pnew;
+	  assert (queue_tail == NULL);
+	  queue_head = queue_tail = pnew;
 	  queue_length = 1;
+	} else {
+	  assert (queue_tail != NULL);
+	  queue_tail->nextp = pnew;
+	  queue_tail = pnew;
+	  queue_length++;
+	  assert (queue_length > 1);
 	}
-	else {
-	  queue_length = 2;	/* head + new one */
-	  plast = queue_head;
-	  while (plast->nextp != NULL) {
-	    plast = plast->nextp;
-	    queue_length++;
-	  }
-	  plast->nextp = pnew;
-	}
-
 
 #if __WIN32__ 
 	LeaveCriticalSection (&dlq_cs);
-#else
-	err = pthread_mutex_unlock (&dlq_mutex);
-	if (err != 0) {
-	  text_color_set(DW_COLOR_ERROR);
-	  dw_printf ("dlq append_to_queue: pthread_mutex_unlock err=%d", err);
-	  perror ("");
-	  exit (1);
-	}
 #endif
 #if DEBUG1
 	text_color_set(DW_COLOR_DEBUG);
@@ -416,7 +396,7 @@ static void append_to_queue (struct dlq_item_s *pnew)
  * and blocking on a write.
  */
 
-	if (queue_length > 10) {
+	if (queue_length > 15) {
 	  text_color_set(DW_COLOR_ERROR);
 	  dw_printf ("Received frame queue is out of control. Length=%d.\n", queue_length);
 	  dw_printf ("Reader thread is probably frozen.\n");
@@ -431,14 +411,6 @@ static void append_to_queue (struct dlq_item_s *pnew)
 #else
 	if (recv_thread_is_waiting) {
 
-	  err = pthread_mutex_lock (&wake_up_mutex);
-	  if (err != 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("dlq append_to_queue: pthread_mutex_lock wu err=%d", err);
-	    perror ("");
-	    exit (1);
-	  }
-
 	  err = pthread_cond_signal (&wake_up_cond);
 	  if (err != 0) {
 	    text_color_set(DW_COLOR_ERROR);
@@ -446,14 +418,14 @@ static void append_to_queue (struct dlq_item_s *pnew)
 	    perror ("");
 	    exit (1);
 	  }
+	}
 
-	  err = pthread_mutex_unlock (&wake_up_mutex);
-	  if (err != 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err);
-	    perror ("");
-	    exit (1);
-	  }
+	err = pthread_mutex_unlock (&dlq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err);
+	  perror ("");
+	  exit (1);
 	}
 #endif
 
@@ -1011,9 +983,29 @@ int dlq_wait_while_empty (double timeout)
 	  dlq_init ();
 	}
 
+#if DEBUG1
+	text_color_set(DW_COLOR_DEBUG);
+	dw_printf ("dlq dlq_wait_while_empty: enter critical section\n");
+#endif
+#if __WIN32__
+	EnterCriticalSection (&dlq_cs);
+#else
+	int err;
+	err = pthread_mutex_lock (&dlq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("dlq append_to_queue: pthread_mutex_lock err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
 
 	if (queue_head == NULL) {
 
+#if __WIN32__
+	  LeaveCriticalSection (&dlq_cs);
+#endif
+
 #if DEBUG
 	  text_color_set(DW_COLOR_DEBUG);
 	  dw_printf ("dlq_wait_while_empty (): prepare to SLEEP...\n");
@@ -1037,18 +1029,15 @@ int dlq_wait_while_empty (double timeout)
 	  else {
 	    WaitForSingleObject (wake_up_event, INFINITE);
 	  }
+	} else {
+#if __WIN32__
+	  LeaveCriticalSection (&dlq_cs);
+#endif
+	}
 
 #else
 	  int err;
 
-	  err = pthread_mutex_lock (&wake_up_mutex);
-	  if (err != 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("dlq_wait_while_empty: pthread_mutex_lock wu err=%d", err);
-	    perror ("");
-	    exit (1);
-	  }
-
 	  recv_thread_is_waiting = 1;
 	  if (timeout != 0.0) {
 	    struct timespec abstime;
@@ -1056,26 +1045,25 @@ int dlq_wait_while_empty (double timeout)
 	    abstime.tv_sec = (time_t)(long)timeout;
 	    abstime.tv_nsec = (long)((timeout - (long)abstime.tv_sec) * 1000000000.0);
 
-	    err = pthread_cond_timedwait (&wake_up_cond, &wake_up_mutex, &abstime);
+	    err = pthread_cond_timedwait (&wake_up_cond, &dlq_mutex, &abstime);
 	    if (err == ETIMEDOUT) {
 	      timed_out_result = 1;
 	    }
 	  }
 	  else {
-	    err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
+	    err = pthread_cond_wait (&wake_up_cond, &dlq_mutex);
 	  }
 	  recv_thread_is_waiting = 0;
-
-	  err = pthread_mutex_unlock (&wake_up_mutex);
-	  if (err != 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("dlq_wait_while_empty: pthread_mutex_unlock wu err=%d", err);
-	    perror ("");
-	    exit (1);
-	  }
-#endif
 	}
 
+	err = pthread_mutex_unlock (&dlq_mutex);
+	if (err != 0) {
+	  text_color_set(DW_COLOR_ERROR);
+	  dw_printf ("dlq_wait_while_empty: pthread_mutex_unlock wu err=%d", err);
+	  perror ("");
+	  exit (1);
+	}
+#endif
 
 #if DEBUG
 	text_color_set(DW_COLOR_DEBUG);
@@ -1133,6 +1121,14 @@ struct dlq_item_s *dlq_remove (void)
 	if (queue_head != NULL) {
 	  result = queue_head;
 	  queue_head = queue_head->nextp;
+	  queue_length--;
+	  if (queue_head == NULL) {
+	    assert (queue_length == 0);
+	    queue_tail = NULL;
+	  }
+	  if (queue_length == 1) {
+	    assert (queue_head == queue_tail);
+	  }
 	}
 	 
 #if __WIN32__
diff --git a/src/ptt.c b/src/ptt.c
index 5187f1df..a75cb8de 100644
--- a/src/ptt.c
+++ b/src/ptt.c
@@ -162,6 +162,10 @@
 #include <hamlib/rig.h>
 #endif
 
+#ifdef USE_GPIOD
+#include <gpiod.h>
+#endif 
+
 /* So we can have more common code for fd. */
 typedef int HANDLE;
 #define INVALID_HANDLE_VALUE (-1)
@@ -468,6 +472,20 @@ void export_gpio(int ch, int ot, int invert, int direction)
 	    text_color_set(DW_COLOR_ERROR);
 	    dw_printf ("Error writing \"%s\" to %s, errno=%d\n", stemp, gpio_export_path, e);
 	    dw_printf ("%s\n", strerror(e));
+
+	    if (e == 22) {
+	      // It appears that error 22 occurs when sysfs gpio is not available.
+	      // (See https://github.com/wb2osz/direwolf/issues/503)
+	      //
+	      // The solution might be to use the new gpiod approach.
+
+	      dw_printf ("It looks like gpio with sysfs is not supported on this operating system.\n");
+	      dw_printf ("Rather than the following form, in the configuration file,\n");
+	      dw_printf ("    PTT GPIO  %s\n", stemp);
+	      dw_printf ("try using gpiod form instead.  e.g.\n");
+	      dw_printf ("    PTT GPIOD  gpiochip0  %s\n", stemp);
+	      dw_printf ("You can get a list of gpio chip names and corresponding I/O lines with \"gpioinfo\" command.\n");
+	    }
 	    exit (1);
 	  }
 	}
@@ -634,6 +652,31 @@ void export_gpio(int ch, int ot, int invert, int direction)
 	get_access_to_gpio (gpio_value_path);
 }
 
+#if defined(USE_GPIOD)
+int gpiod_probe(const char *chip_name, int line_number)
+{
+	struct gpiod_chip *chip;
+	chip = gpiod_chip_open_by_name(chip_name);
+	if (chip == NULL) {
+		text_color_set(DW_COLOR_ERROR);
+		dw_printf ("Can't open GPIOD chip %s.\n", chip_name);
+		return -1;
+	}
+
+	struct gpiod_line *line;
+	line = gpiod_chip_get_line(chip, line_number);
+	if (line == NULL) {
+		text_color_set(DW_COLOR_ERROR);
+		dw_printf ("Can't get GPIOD line %d.\n", line_number);
+		return -1;
+	}
+	if (ptt_debug_level >= 2) {
+		text_color_set(DW_COLOR_DEBUG);
+		dw_printf("GPIOD probe OK. Chip: %s line: %d\n", chip_name, line_number);
+	}
+	return 0;
+}
+#endif   /* USE_GPIOD */
 #endif   /* not __WIN32__ */
 
 
@@ -650,7 +693,8 @@ void export_gpio(int ch, int ot, int invert, int direction)
  *			ptt_method	Method for PTT signal. 
  *					PTT_METHOD_NONE - not configured.  Could be using VOX. 
  *					PTT_METHOD_SERIAL - serial (com) port. 
- *					PTT_METHOD_GPIO - general purpose I/O. 
+ *					PTT_METHOD_GPIO - general purpose I/O (sysfs). 
+ *					PTT_METHOD_GPIOD - general purpose I/O (libgpiod). 
  *					PTT_METHOD_LPT - Parallel printer port. 
  *                  			PTT_METHOD_HAMLIB - HAMLib rig control.
  *					PTT_METHOD_CM108 - GPIO pins of CM108 etc. USB Audio.
@@ -729,12 +773,13 @@ void ptt_init (struct audio_s *audio_config_p)
 	    if (ptt_debug_level >= 2) {
 
 	      text_color_set(DW_COLOR_DEBUG);
-              dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n",
+              dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, name=%s, gpio=%d, lpt_bit=%d, invert=%d\n",
 		ch,
 		otnames[ot],
 		audio_config_p->achan[ch].octrl[ot].ptt_method, 
 		audio_config_p->achan[ch].octrl[ot].ptt_device,
 		audio_config_p->achan[ch].octrl[ot].ptt_line,
+		audio_config_p->achan[ch].octrl[ot].out_gpio_name,
 		audio_config_p->achan[ch].octrl[ot].out_gpio_num,
 		audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit,
 		audio_config_p->achan[ch].octrl[ot].ptt_invert);
@@ -880,7 +925,28 @@ void ptt_init (struct audio_s *audio_config_p)
 	if (using_gpio) {
 	  get_access_to_gpio ("/sys/class/gpio/export");
 	}
-
+#if defined(USE_GPIOD)
+    // GPIOD
+	for (ch = 0; ch < MAX_CHANS; ch++) {
+	  if (save_audio_config_p->chan_medium[ch] == MEDIUM_RADIO) {
+	    for (int ot = 0; ot < NUM_OCTYPES; ot++) {
+	      if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIOD) {
+	        const char *chip_name = audio_config_p->achan[ch].octrl[ot].out_gpio_name;
+	        int line_number = audio_config_p->achan[ch].octrl[ot].out_gpio_num;
+	        int rc = gpiod_probe(chip_name, line_number);
+	        if (rc < 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Disable PTT for channel %d\n", ch);
+	          audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
+	        } else {
+	          // Set initial state off ptt_set will invert output signal if appropriate.
+	          ptt_set (ot, ch, 0);
+	        }
+	      }
+	    }
+	  }
+	}
+#endif /* USE_GPIOD */
 /*
  * We should now be able to create the device nodes for 
  * the pins we want to use.
@@ -1298,6 +1364,18 @@ void ptt_set (int ot, int chan, int ptt_signal)
 	  close (fd);
 
 	}
+
+#if defined(USE_GPIOD)
+	if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIOD) {
+		const char *chip = save_audio_config_p->achan[chan].octrl[ot].out_gpio_name;
+		int line = save_audio_config_p->achan[chan].octrl[ot].out_gpio_num;
+		int rc = gpiod_ctxless_set_value(chip, line, ptt, false, "direwolf", NULL, NULL);
+		if (ptt_debug_level >= 1) {
+			text_color_set(DW_COLOR_DEBUG);
+			dw_printf("PTT_METHOD_GPIOD chip: %s line: %d ptt: %d  rc: %d\n", chip, line, ptt, rc);
+		}
+	}
+#endif /* USE_GPIOD */
 #endif
 	
 /*