diff --git a/applications/meta/application.fam b/applications/meta/application.fam
index a447b94a..5f4d1670 100644
--- a/applications/meta/application.fam
+++ b/applications/meta/application.fam
@@ -37,5 +37,6 @@ App(
         "music_player",
         "snake_game",
         "bt_hid",
+        "sensor_ds18x20",
     ],
 )
diff --git a/applications/sensor_ds18x20/application.fam b/applications/sensor_ds18x20/application.fam
new file mode 100644
index 00000000..8fa123c7
--- /dev/null
+++ b/applications/sensor_ds18x20/application.fam
@@ -0,0 +1,11 @@
+App(
+    appid="sensor_ds18x20",
+    name="Sensor DS18x20",
+    apptype=FlipperAppType.PLUGIN,
+    entry_point="sensor_ds18x20_app",
+    cdefines=["APP_SENSOR_DS18X20"],
+    requires=["gui"],
+    stack_size=1 * 1024,
+    icon="A_Plugins_14",
+    order=80,
+)
diff --git a/applications/sensor_ds18x20/sensor_ds18x20.c b/applications/sensor_ds18x20/sensor_ds18x20.c
new file mode 100644
index 00000000..2617043e
--- /dev/null
+++ b/applications/sensor_ds18x20/sensor_ds18x20.c
@@ -0,0 +1,478 @@
+#include <furi.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_resources.h>
+#include <furi_hal_power.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <one_wire/one_wire_host.h>
+#include <one_wire/maxim_crc.h>
+
+#define GPIO_ITEM_COUNT 8
+
+/* We're looking at:
+ *
+ * ./MAX31850_DallasTemp/DallasTemperature.h:#define DS18S20MODEL 0x10  //!< DS18S20 model ID
+ * ./MAX31850_DallasTemp/DallasTemperature.h-#define DS18B20MODEL 0x28  //!< DS18B20 model ID
+ *
+ * 28ff:3227:8116:03d4 -> DS18B20MODEL
+ *
+ * With scratch_pad:
+ *
+ * 5005:4b46:7fff:0c10
+ * TEMP:ALRM:CC..:....
+ *
+		// byte 0: temperature LSB
+		// byte 1: temperature MSB
+		// byte 2: high alarm temp
+		// byte 3: low alarm temp
+		// byte 4: DS18S20: store for crc / DS18B20 & DS1822: configuration register
+		// byte 5: internal use & crc
+		// byte 6: DS18S20: COUNT_REMAIN / DS18B20 & DS1822: store for crc
+		// byte 7: DS18S20: COUNT_PER_C / DS18B20 & DS1822: store for crc
+ *
+ */
+
+
+typedef enum {
+    EventTypeTick,
+    EventTypeKey,
+} EventType;
+
+typedef struct {
+    EventType type;
+    InputEvent input;
+} AppEvent;
+
+typedef struct {
+    OneWireHost* host;
+    uint8_t device_address[8];
+    uint8_t scratch_pad[8];
+    unsigned short count;
+    unsigned short debug;
+    unsigned short debug_readcrc;
+    unsigned short debug_calccrc;
+    float celsius;
+    uint8_t gpio_pin_nr;
+} AppContext;
+
+typedef struct {
+    const char* name;
+    const GpioPin* pin;
+} GpioItem;
+
+static const GpioItem gpio_item[GPIO_ITEM_COUNT] = {
+    {"1.2: PA7", &gpio_ext_pa7},
+    {"1.3: PA6", &gpio_ext_pa6},
+    {"1.4: PA4", &gpio_ext_pa4},
+    {"1.5: PB3", &gpio_ext_pb3},
+    {"1.6: PB2", &gpio_ext_pb2},
+    {"1.7: PC3", &gpio_ext_pc3},
+    {"2.7: PC1", &gpio_ext_pc1},
+    {"2.8: PC0", &gpio_ext_pc0},
+};
+
+static const char* get_current_pin_name(AppContext* ctx) {
+    furi_assert(ctx->gpio_pin_nr < GPIO_ITEM_COUNT);
+    return gpio_item[ctx->gpio_pin_nr].name;
+}
+
+static const GpioPin* get_current_pin(AppContext* ctx) {
+    furi_assert(ctx->gpio_pin_nr < GPIO_ITEM_COUNT);
+    return gpio_item[ctx->gpio_pin_nr].pin;
+}
+
+//static void gpio_item_configure_pin(uint8_t index, GpioMode mode) {
+//    furi_assert(index < GPIO_ITEM_COUNT);
+//    furi_hal_gpio_write(gpio_item[index].pin, false);
+//    furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
+//}
+//
+//static void gpio_item_configure_all_pins(GpioMode mode) {
+//    for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) {
+//        gpio_item_configure_pin(i, mode);
+//    }
+//}
+//
+//static void gpio_item_set_pin(uint8_t index, bool level) {
+//    furi_assert(index < GPIO_ITEM_COUNT);
+//    furi_hal_gpio_write(gpio_item[index].pin, level);
+//}
+//
+//static const char* gpio_item_get_pin_name(uint8_t index) {
+//    furi_assert(index < GPIO_ITEM_COUNT + 1);
+//    if(index == GPIO_ITEM_COUNT) {
+//        return "ALL";
+//    } else {
+//        return gpio_item[index].name;
+//    }
+//}
+
+#define ONEWIRE_SELECT 0x55 // "choose rom"
+
+// Model IDs
+#define DS18S20MODEL 0x10  //!< DS18S20 model ID
+#define DS18B20MODEL 0x28  //!< DS18B20 model ID
+#define DS1822MODEL 0x22   //!< DS1822 model ID
+#define MAX31850MODEL 0x3B //!< MAX31850 model ID
+
+// OneWire commands
+#define STARTCONVO                                                             \
+  0x44 //!< Tells device to take a temperature reading and put it on the
+       //!< scratchpad
+#define COPYSCRATCH 0x48     //!< Copy EEPROM
+#define READSCRATCH 0xBE     //!< Read EEPROM
+#define WRITESCRATCH 0x4E    //!< Write to EEPROM
+#define RECALLSCRATCH 0xB8   //!< Reload from last known
+#define READPOWERSUPPLY 0xB4 //!< Determine if device needs parasite power
+#define ALARMSEARCH 0xEC     //!< Query bus for devices with an alarm condition
+
+// Scratchpad locations
+#define TEMP_LSB 0        //!< Temperature LSB byte location
+#define TEMP_MSB 1        //!< Temperature MSB byte location
+#define HIGH_ALARM_TEMP 2 //!< High alarm temp byte location
+#define LOW_ALARM_TEMP 3  //!< Low alarm temp byte location
+#define CONFIGURATION                                                          \
+  4 //!< DS18S20: store for CRC. DS18B20 & DS1822: configuration register
+#define INTERNAL_BYTE 5 //!< Internal use & CRC
+#define COUNT_REMAIN                                                           \
+  6 //!< DS18S20: COUNT_REMAIN, DS18B20 & DS1822: store for CRC
+#define COUNT_PER_C 7 //!< DS18S20: COUNT_PER_C. DS18B20 & DS1822: store for crc
+
+// Device resolution
+#define TEMP_9_BIT 0x1F  //!<  9 bit resolution
+#define TEMP_10_BIT 0x3F //!< 10 bit resolution
+#define TEMP_11_BIT 0x5F //!< 11 bit resolution
+#define TEMP_12_BIT 0x7F //!< 12 bit resolution
+
+// Error Codes
+#define DEVICE_DISCONNECTED -127 //!< Device disconnected error code
+
+#define NAN -666.66f
+
+
+/* from MAX31850_DallasTemp / DallasTemperature.cpp */
+static float scratch_pad_to_temperature(uint8_t *deviceAddress, uint8_t *scratchPad) {
+    int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
+
+    switch (deviceAddress[0]) {
+	case MAX31850MODEL:
+	    if (scratchPad[0] & 0x1) {
+		return NAN;
+	    } else {
+		return (float)rawTemperature * 0.0625;
+	    }
+	    break;
+	case DS18B20MODEL:
+	case DS1822MODEL:
+	    switch (scratchPad[CONFIGURATION]) {
+		case TEMP_12_BIT:
+		    return (float)rawTemperature * 0.0625;
+		    break;
+		case TEMP_11_BIT:
+		    return (float)(rawTemperature >> 1) * 0.125;
+		    break;
+		case TEMP_10_BIT:
+		    return (float)(rawTemperature >> 2) * 0.25;
+		    break;
+		case TEMP_9_BIT:
+		    return (float)(rawTemperature >> 3) * 0.5;
+		    break;
+	    }
+	    break;
+	case DS18S20MODEL:
+	    /*
+
+	       Resolutions greater than 9 bits can be calculated using the data from
+	       the temperature, COUNT REMAIN and COUNT PER �C registers in the
+	       scratchpad. Note that the COUNT PER �C register is hard-wired to 16
+	       (10h). After reading the scratchpad, the TEMP_READ value is obtained
+	       by truncating the 0.5�C bit (bit 0) from the temperature data. The
+	       extended resolution temperature can then be calculated using the
+	       following equation:
+
+	       COUNT_PER_C - COUNT_REMAIN
+	       TEMPERATURE = TEMP_READ - 0.25 + --------------------------
+	       COUNT_PER_C
+	       */
+
+	    // Good spot. Thanks Nic Johns for your contribution
+	    return (float)(rawTemperature >> 1) - 0.25 +
+		((float)(scratchPad[COUNT_PER_C] - scratchPad[COUNT_REMAIN]) /
+		 (float)scratchPad[COUNT_PER_C]);
+	    break;
+    }
+    return NAN;
+}
+
+static void sensor_select(AppContext* context) {
+    onewire_host_write(context->host, ONEWIRE_SELECT);
+    for(uint8_t i = 0; i < sizeof(context->device_address); i++) {
+	onewire_host_write(context->host, context->device_address[i]);
+    }
+}
+
+static bool sensor_start_convo(AppContext* context) {
+    if (!onewire_host_reset(context->host)) {
+	return false;
+    }
+    sensor_select(context);
+    onewire_host_write(context->host, STARTCONVO);
+    return true;
+}
+
+static bool sensor_read_scratch(AppContext* context) {
+    if (!onewire_host_reset(context->host)) {
+	return false;
+    }
+    sensor_select(context);
+    onewire_host_write(context->host, READSCRATCH);
+
+    for (uint8_t i = 0; i < sizeof(context->scratch_pad); ++i) {
+	// byte 0: temperature LSB
+	// byte 1: temperature MSB
+	// byte 2: high alarm temp
+	// byte 3: low alarm temp
+	// byte 4: DS18S20: store for crc / DS18B20 & DS1822: configuration register
+	// byte 5: internal use & crc
+	// byte 6: DS18S20: COUNT_REMAIN / DS18B20 & DS1822: store for crc
+	// byte 7: DS18S20: COUNT_PER_C / DS18B20 & DS1822: store for crc
+	context->scratch_pad[i] = onewire_host_read(context->host);
+    }
+
+    uint8_t read_crc = onewire_host_read(context->host);
+    uint8_t calc_crc = maxim_crc8(context->scratch_pad, sizeof(context->scratch_pad), MAXIM_CRC8_INIT);
+
+    context->debug_readcrc = read_crc;
+    context->debug_calccrc = calc_crc;
+
+    return (calc_crc == read_crc);
+}
+
+static unsigned short sensor_ds18x20_read_scratch(AppContext *context) {
+    bool found = false;
+    bool result = false;
+
+    furi_hal_power_enable_otg();
+    furi_delay_ms(100);
+
+    onewire_host_start(context->host);
+    furi_delay_ms(100);
+
+    FURI_CRITICAL_ENTER();
+
+    // search devices
+    context->debug = 0;
+    if (onewire_host_search(context->host, context->device_address, NORMAL_SEARCH)) {
+        onewire_host_reset_search(context->host);
+
+	found = true;
+	context->debug |= 1;
+
+	if (sensor_start_convo(context)) {
+	    context->debug |= 2;
+
+	    //blockTillConversionComplete
+	    if (sensor_read_scratch(context)) {
+		context->debug |= 4;
+
+	        //uint8_t timing = context->scratch_pad[0];
+		//for (uint8_t i = 0; i < sizeof(context->scratch_pad); i++) {
+		//    if (!sensor_read_scratch(context)) {
+		//	break;
+		//    }
+		//    if (
+		result = true;
+	    }
+	    onewire_host_reset(context->host);
+
+	} else {
+	    context->scratch_pad[7] = (uint8_t)context->count;
+	    context->scratch_pad[6] = 0xff;
+	}
+    } else {
+        onewire_host_reset_search(context->host);
+	context->device_address[7] = (uint8_t)context->count;
+	context->device_address[6] = 0xff;
+    }
+    
+    onewire_host_stop(context->host);
+    FURI_CRITICAL_EXIT();
+
+    furi_hal_power_disable_otg();
+
+    return found && result;
+}
+
+static void sensor_ds18x20_render_callback(Canvas* const canvas, void* ctx) {
+    //const SnakeState* snake_state = acquire_mutex((ValueMutex*)ctx, 25);
+    //if(snake_state == NULL) {
+    //    return;
+    //}
+
+    // Before the function is called, the state is set with the canvas_reset(canvas)
+
+    // Frame
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+
+    // Screen is 128x64 px
+    //canvas_set_color(canvas, ColorWhite);
+    //canvas_draw_box(canvas, 34, 20, 62, 24);
+    //canvas_set_color(canvas, ColorBlack);
+    //canvas_draw_frame(canvas, 34, 20, 62, 24);
+
+    canvas_set_font(canvas, FontSecondary);
+
+    char buf[64];
+    AppContext *context = (AppContext*)ctx;
+
+    snprintf(buf, sizeof(buf), "P: %s", get_current_pin_name(context));
+    canvas_draw_str(canvas, 4, 11, buf);
+
+    snprintf(buf, sizeof(buf), "D: %02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx",
+	context->device_address[0], context->device_address[1], context->device_address[2], context->device_address[3],
+	context->device_address[4], context->device_address[5], context->device_address[6], context->device_address[7]);
+    canvas_draw_str(canvas, 4, 21, buf);
+
+    snprintf(buf, sizeof(buf), "S: %02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx",
+	context->scratch_pad[0], context->scratch_pad[1], context->scratch_pad[2], context->scratch_pad[3],
+	context->scratch_pad[4], context->scratch_pad[5], context->scratch_pad[6], context->scratch_pad[7]);
+    canvas_draw_str(canvas, 4, 31, buf);
+
+    snprintf(buf, sizeof(buf), "C: %.1f", (double)context->celsius);
+    canvas_draw_str(canvas, 4, 41, buf);
+
+    if (context->debug_readcrc != context->debug_calccrc) {
+	snprintf(buf, sizeof(buf), "X: %hu, crc=%02hx/%02hx", context->count, context->debug_readcrc, context->debug_calccrc);
+    } else {
+	snprintf(buf, sizeof(buf), "X: %hu, state=%hx", context->count, context->debug);
+    }
+    canvas_draw_str(canvas, 4, 51, buf);
+
+#if 0    
+    canvas_set_font(canvas, FontSecondary);
+    char buffer[12];
+    snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7);
+    canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer);
+#endif
+}
+
+static void sensor_ds18x20_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+    furi_assert(event_queue);
+
+    AppEvent event = {.type = EventTypeKey, .input = *input_event};
+    furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void sensor_ds18x20_update_timer_callback(FuriMessageQueue* event_queue) {
+    furi_assert(event_queue);
+
+    AppEvent event = {.type = EventTypeTick};
+    furi_message_queue_put(event_queue, &event, 0);
+}
+
+int32_t sensor_ds18x20_app(void* p) {
+    UNUSED(p);
+
+    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
+
+    AppContext* context = malloc(sizeof(AppContext)); // XXX: no checking for OOM
+    memset(context, 0, sizeof(AppContext));
+
+    context->gpio_pin_nr = 0;
+    context->host = onewire_host_alloc(get_current_pin(context));
+
+    //SnakeState* snake_state = malloc(sizeof(SnakeState));
+    //ValueMutex state_mutex;
+    //if(!init_mutex(&state_mutex, snake_state, sizeof(SnakeState))) {
+    //    FURI_LOG_E("SnakeGame", "cannot create mutex\r\n");
+    //    free(snake_state);
+    //    return 255;
+    //}
+
+    //unsigned short counter = 0;
+    //gpio_item_configure_all_pins(GpioModeOutputPushPull);
+    //gpio_item_set_pin(0, true);
+
+    ViewPort* view_port = view_port_alloc();
+    view_port_draw_callback_set(view_port, sensor_ds18x20_render_callback, context);
+    view_port_input_callback_set(view_port, sensor_ds18x20_input_callback, event_queue);
+
+    FuriTimer* timer =
+        furi_timer_alloc(sensor_ds18x20_update_timer_callback, FuriTimerTypePeriodic, event_queue);
+    furi_timer_start(timer, furi_kernel_get_tick_frequency() * 3);
+
+    // Open GUI and register view_port
+    Gui* gui = furi_record_open(RECORD_GUI);
+    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+    AppEvent event;
+    for(bool processing = true; processing;) {
+        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+
+	//SnakeState* snake_state = (SnakeState*)acquire_mutex_block(&state_mutex);
+
+        if(event_status == FuriStatusOk) {
+            // press events
+            if(event.type == EventTypeKey) {
+                if(event.input.type == InputTypePress) {
+                    switch(event.input.key) {
+                    case InputKeyRight:
+			onewire_host_free(context->host);
+			context->gpio_pin_nr = (context->gpio_pin_nr + 1) % GPIO_ITEM_COUNT;
+			context->host = onewire_host_alloc(get_current_pin(context));
+                        break;
+                    case InputKeyLeft:
+			onewire_host_free(context->host);
+			context->gpio_pin_nr = (context->gpio_pin_nr + GPIO_ITEM_COUNT - 1) % GPIO_ITEM_COUNT;
+			context->host = onewire_host_alloc(get_current_pin(context));
+                        break;
+#if 0
+                    case InputKeyUp:
+                        snake_state->nextMovement = DirectionUp;
+                        break;
+                    case InputKeyDown:
+                        snake_state->nextMovement = DirectionDown;
+                        break;
+                    case InputKeyOk:
+                        if(snake_state->state == GameStateGameOver) {
+                            sensor_ds18x20_init_game(snake_state);
+                        }
+                        break;
+#endif
+                    case InputKeyBack:
+                        processing = false;
+                        break;
+		    default:
+			context->count++;
+			break;
+                    }
+                }
+            } else if(event.type == EventTypeTick) {
+		float temperature = NAN;
+		if (sensor_ds18x20_read_scratch(context)) {
+		    temperature = scratch_pad_to_temperature(context->device_address, context->scratch_pad);
+		}
+		context->celsius = temperature;
+		context->count++;
+	    }
+        } else {
+            // event timeout
+        }
+
+        view_port_update(view_port);
+	//release_mutex(&state_mutex, snake_state);
+    }
+
+    furi_timer_free(timer);
+    view_port_enabled_set(view_port, false);
+    gui_remove_view_port(gui, view_port);
+    furi_record_close(RECORD_GUI);
+    view_port_free(view_port);
+    furi_message_queue_free(event_queue);
+    //delete_mutex(&state_mutex);
+    onewire_host_free(context->host);
+    free(context);
+
+    return 0;
+}
+
