diff --git a/include/list.h b/include/list.h
index 0e4342a5..6e40744a 100644
--- a/include/list.h
+++ b/include/list.h
@@ -9,6 +9,9 @@
     (at your option) any later version.
 */
 
+#ifndef INCLUDE_LIST_H_
+#define INCLUDE_LIST_H_
+
 #include <stddef.h>
 
 /// Dynamically growing list, elems is always NULL terminated, call list_ensure_size() to alloc elems.
@@ -37,3 +40,5 @@ void list_clear(list_t *list, list_elem_free_fn elem_free);
 
 /// Clear the list, free backing, does not free list itself.
 void list_free_elems(list_t *list, list_elem_free_fn elem_free);
+
+#endif /* INCLUDE_LIST_H_ */
diff --git a/include/r_api.h b/include/r_api.h
new file mode 100644
index 00000000..0def613a
--- /dev/null
+++ b/include/r_api.h
@@ -0,0 +1,29 @@
+/** @file
+    Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR.
+
+    Copyright (C) 2019 Christian W. Zuckschwerdt <zany@triq.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+*/
+
+#ifndef INCLUDE_R_API_H_
+#define INCLUDE_R_API_H_
+
+#include <stdint.h>
+
+struct r_cfg;
+
+/* general */
+
+char const *version_string(void);
+
+struct r_cfg *r_create_cfg(void);
+
+void r_init_cfg(struct r_cfg *cfg);
+
+void r_free_cfg(struct r_cfg *cfg);
+
+#endif /* INCLUDE_R_API_H_ */
diff --git a/include/r_private.h b/include/r_private.h
new file mode 100644
index 00000000..60720532
--- /dev/null
+++ b/include/r_private.h
@@ -0,0 +1,52 @@
+/** @file
+    Definition of r_private state structure.
+*/
+
+#ifndef INCLUDE_R_PRIVATE_H_
+#define INCLUDE_R_PRIVATE_H_
+
+#include <stdint.h>
+#include <time.h>
+#include "list.h"
+#include "baseband.h"
+#include "pulse_detect.h"
+#include "fileformat.h"
+#include "samp_grab.h"
+#include "am_analyze.h"
+#include "rtl_433.h"
+
+struct dm_state {
+    int32_t level_limit;
+    int16_t am_buf[MAXIMAL_BUF_LENGTH];  // AM demodulated signal (for OOK decoding)
+    union {
+        // These buffers aren't used at the same time, so let's use a union to save some memory
+        int16_t fm[MAXIMAL_BUF_LENGTH];  // FM demodulated signal (for FSK decoding)
+        uint16_t temp[MAXIMAL_BUF_LENGTH];  // Temporary buffer (to be optimized out..)
+    } buf;
+    uint8_t u8_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer
+    float f32_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer
+    int sample_size; // CU8: 1, CS16: 2
+    pulse_detect_t *pulse_detect;
+    filter_state_t lowpass_filter_state;
+    demodfm_state_t demod_FM_state;
+    int enable_FM_demod;
+    samp_grab_t *samp_grab;
+    am_analyze_t *am_analyze;
+    int analyze_pulses;
+    file_info_t load_info;
+    list_t dumper;
+    int hop_time;
+
+    /* Protocol states */
+    list_t r_devs;
+
+    pulse_data_t    pulse_data;
+    pulse_data_t    fsk_pulse_data;
+    unsigned frame_event_count;
+    unsigned frame_start_ago;
+    unsigned frame_end_ago;
+    struct timeval now;
+    float sample_file_pos;
+};
+
+#endif /* INCLUDE_R_PRIVATE_H_ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c1abf747..7d0f882d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,6 +18,7 @@ add_executable(rtl_433
 	output_mqtt.c
 	pulse_demod.c
 	pulse_detect.c
+	r_api.c
 	r_util.c
 	rtl_433.c
 	samp_grab.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1c65e526..ab64ce9e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,6 +19,7 @@ rtl_433_SOURCES      = abuf.c \
                        output_mqtt.c \
                        pulse_demod.c \
                        pulse_detect.c \
+                       r_api.c \
                        r_util.c \
                        rtl_433.c \
                        samp_grab.c \
diff --git a/src/r_api.c b/src/r_api.c
new file mode 100644
index 00000000..2398a587
--- /dev/null
+++ b/src/r_api.c
@@ -0,0 +1,128 @@
+/** @file
+    Generic RF data receiver and decoder for ISM band devices using RTL-SDR and SoapySDR.
+
+    Copyright (C) 2019 Christian W. Zuckschwerdt <zany@triq.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "r_api.h"
+#include "rtl_433.h"
+#include "r_private.h"
+#include "sdr.h"
+#include "data.h"
+
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+#define F_OK 0
+#endif
+#endif
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
+#ifndef _MSC_VER
+#include <getopt.h>
+#else
+#include "getopt/getopt.h"
+#endif
+
+char const *version_string(void)
+{
+    return "rtl_433"
+#ifdef GIT_VERSION
+#define STR_VALUE(arg) #arg
+#define STR_EXPAND(s) STR_VALUE(s)
+            " version " STR_EXPAND(GIT_VERSION)
+            " branch " STR_EXPAND(GIT_BRANCH)
+            " at " STR_EXPAND(GIT_TIMESTAMP)
+#undef STR_VALUE
+#undef STR_EXPAND
+#else
+            " version unknown"
+#endif
+            " inputs file rtl_tcp"
+#ifdef RTLSDR
+            " RTL-SDR"
+#endif
+#ifdef SOAPYSDR
+            " SoapySDR"
+#endif
+            ;
+}
+
+void r_init_cfg(r_cfg_t *cfg)
+{
+    cfg->out_block_size  = DEFAULT_BUF_LENGTH;
+    cfg->samp_rate       = DEFAULT_SAMPLE_RATE;
+    cfg->conversion_mode = CONVERT_NATIVE;
+
+    list_ensure_size(&cfg->in_files, 100);
+    list_ensure_size(&cfg->output_handler, 16);
+
+    cfg->demod = calloc(1, sizeof(*cfg->demod));
+    if (!cfg->demod) {
+        fprintf(stderr, "Could not create demod!\n");
+        exit(1);
+    }
+
+    cfg->demod->level_limit = DEFAULT_LEVEL_LIMIT;
+    cfg->demod->hop_time    = DEFAULT_HOP_TIME;
+
+    list_ensure_size(&cfg->demod->r_devs, 100);
+    list_ensure_size(&cfg->demod->dumper, 32);
+}
+
+r_cfg_t *r_create_cfg(void)
+{
+    r_cfg_t *cfg = calloc(1, sizeof(*cfg));
+    if (!cfg) {
+        fprintf(stderr, "Could not create cfg!\n");
+        exit(1);
+    }
+
+    r_init_cfg(cfg);
+
+    return cfg;
+}
+
+void r_free_cfg(r_cfg_t *cfg)
+{
+    if (cfg->dev)
+        sdr_deactivate(cfg->dev);
+    if (cfg->dev)
+        sdr_close(cfg->dev);
+
+    for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) {
+        file_info_t const *dumper = *iter;
+        if (dumper->file && (dumper->file != stdout))
+            fclose(dumper->file);
+    }
+    list_free_elems(&cfg->demod->dumper, free);
+
+    list_free_elems(&cfg->demod->r_devs, free);
+
+    if (cfg->demod->am_analyze)
+        am_analyze_free(cfg->demod->am_analyze);
+
+    pulse_detect_free(cfg->demod->pulse_detect);
+
+    free(cfg->demod);
+
+    list_free_elems(&cfg->output_handler, (list_elem_free_fn)data_output_free);
+
+    list_free_elems(&cfg->in_files, NULL);
+
+    //free(cfg);
+}
diff --git a/src/rtl_433.c b/src/rtl_433.c
index e451fff2..b34ee2cf 100644
--- a/src/rtl_433.c
+++ b/src/rtl_433.c
@@ -30,8 +30,10 @@
 #include <signal.h>
 
 #include "rtl_433.h"
+#include "r_private.h"
 #include "r_device.h"
 #include "rtl_433_devices.h"
+#include "r_api.h"
 #include "sdr.h"
 #include "baseband.h"
 #include "pulse_detect.h"
@@ -65,68 +67,10 @@
 #include "getopt/getopt.h"
 #endif
 
-char const *version_string(void)
-{
-    return "rtl_433"
-#ifdef GIT_VERSION
-#define STR_VALUE(arg) #arg
-#define STR_EXPAND(s) STR_VALUE(s)
-            " version " STR_EXPAND(GIT_VERSION)
-            " branch " STR_EXPAND(GIT_BRANCH)
-            " at " STR_EXPAND(GIT_TIMESTAMP)
-#undef STR_VALUE
-#undef STR_EXPAND
-#else
-            " version unknown"
-#endif
-            " inputs file rtl_tcp"
-#ifdef RTLSDR
-            " RTL-SDR"
-#endif
-#ifdef SOAPYSDR
-            " SoapySDR"
-#endif
-            ;
-}
-
 r_device *flex_create_device(char *spec); // maybe put this in some header file?
 
 void data_acquired_handler(r_device *r_dev, data_t *data);
 
-struct dm_state {
-    int32_t level_limit;
-    int16_t am_buf[MAXIMAL_BUF_LENGTH];  // AM demodulated signal (for OOK decoding)
-    union {
-        // These buffers aren't used at the same time, so let's use a union to save some memory
-        int16_t fm[MAXIMAL_BUF_LENGTH];  // FM demodulated signal (for FSK decoding)
-        uint16_t temp[MAXIMAL_BUF_LENGTH];  // Temporary buffer (to be optimized out..)
-    } buf;
-    uint8_t u8_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer
-    float f32_buf[MAXIMAL_BUF_LENGTH]; // format conversion buffer
-    int sample_size; // CU8: 1, CS16: 2
-    pulse_detect_t *pulse_detect;
-    filter_state_t lowpass_filter_state;
-    demodfm_state_t demod_FM_state;
-    int enable_FM_demod;
-    samp_grab_t *samp_grab;
-    am_analyze_t *am_analyze;
-    int analyze_pulses;
-    file_info_t load_info;
-    list_t dumper;
-    int hop_time;
-
-    /* Protocol states */
-    list_t r_devs;
-
-    pulse_data_t    pulse_data;
-    pulse_data_t    fsk_pulse_data;
-    unsigned frame_event_count;
-    unsigned frame_start_ago;
-    unsigned frame_end_ago;
-    struct timeval now;
-    float sample_file_pos;
-};
-
 static void print_version(void)
 {
     fprintf(stderr, "%s\n", version_string());
@@ -1669,71 +1613,6 @@ static void parse_conf_option(r_cfg_t *cfg, int opt, char *arg)
     }
 }
 
-void r_init_cfg(r_cfg_t *cfg)
-{
-    cfg->out_block_size  = DEFAULT_BUF_LENGTH;
-    cfg->samp_rate       = DEFAULT_SAMPLE_RATE;
-    cfg->conversion_mode = CONVERT_NATIVE;
-
-    list_ensure_size(&cfg->in_files, 100);
-    list_ensure_size(&cfg->output_handler, 16);
-
-    cfg->demod = calloc(1, sizeof(*cfg->demod));
-    if (!cfg->demod) {
-        fprintf(stderr, "Could not create demod!\n");
-        exit(1);
-    }
-
-    cfg->demod->level_limit = DEFAULT_LEVEL_LIMIT;
-    cfg->demod->hop_time    = DEFAULT_HOP_TIME;
-
-    list_ensure_size(&cfg->demod->r_devs, 100);
-    list_ensure_size(&cfg->demod->dumper, 32);
-}
-
-r_cfg_t *r_create_cfg(void)
-{
-    r_cfg_t *cfg = calloc(1, sizeof(*cfg));
-    if (!cfg) {
-        fprintf(stderr, "Could not create cfg!\n");
-        exit(1);
-    }
-
-    r_init_cfg(cfg);
-
-    return cfg;
-}
-
-void r_free_cfg(r_cfg_t *cfg)
-{
-    if (cfg->dev)
-        sdr_deactivate(cfg->dev);
-    if (cfg->dev)
-        sdr_close(cfg->dev);
-
-    for (void **iter = cfg->demod->dumper.elems; iter && *iter; ++iter) {
-        file_info_t const *dumper = *iter;
-        if (dumper->file && (dumper->file != stdout))
-            fclose(dumper->file);
-    }
-    list_free_elems(&cfg->demod->dumper, free);
-
-    list_free_elems(&cfg->demod->r_devs, free);
-
-    if (cfg->demod->am_analyze)
-        am_analyze_free(cfg->demod->am_analyze);
-
-    pulse_detect_free(cfg->demod->pulse_detect);
-
-    free(cfg->demod);
-
-    list_free_elems(&cfg->output_handler, (list_elem_free_fn)data_output_free);
-
-    list_free_elems(&cfg->in_files, NULL);
-
-    //free(cfg);
-}
-
 // well-known fields "time", "msg" and "codes" are used to output general decoder messages
 // well-known field "bits" is only used when verbose bits (-M bits) is requested
 // well-known field "tag" is only used when output tagging is requested
diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj
index 3fb365ca..084fb909 100644
--- a/vs15/rtl_433.vcxproj
+++ b/vs15/rtl_433.vcxproj
@@ -102,6 +102,8 @@
     <ClInclude Include="..\include\output_mqtt.h" />
     <ClInclude Include="..\include\pulse_demod.h" />
     <ClInclude Include="..\include\pulse_detect.h" />
+    <ClInclude Include="..\include\r_api.h" />
+    <ClInclude Include="..\include\r_private.h" />
     <ClInclude Include="..\include\r_device.h" />
     <ClInclude Include="..\include\r_util.h" />
     <ClInclude Include="..\include\rtl_433.h" />
@@ -129,6 +131,7 @@
     <ClCompile Include="..\src\output_mqtt.c" />
     <ClCompile Include="..\src\pulse_demod.c" />
     <ClCompile Include="..\src\pulse_detect.c" />
+    <ClCompile Include="..\src\r_api.c" />
     <ClCompile Include="..\src\r_util.c" />
     <ClCompile Include="..\src\rtl_433.c" />
     <ClCompile Include="..\src\samp_grab.c" />
diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters
index ef86ce4e..3b84c849 100644
--- a/vs15/rtl_433.vcxproj.filters
+++ b/vs15/rtl_433.vcxproj.filters
@@ -68,6 +68,12 @@
     <ClInclude Include="..\include\pulse_detect.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\include\r_api.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\include\r_private.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\include\r_device.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -145,6 +151,9 @@
     <ClCompile Include="..\src\pulse_detect.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\r_api.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\r_util.c">
       <Filter>Source Files</Filter>
     </ClCompile>