From f928a4ff8cf215b1b661808a0e5b1cbe9992fff6 Mon Sep 17 00:00:00 2001
From: MightyPork <ondra@ondrovo.com>
Date: Mon, 11 May 2015 23:09:32 +0200
Subject: [PATCH] stuff

---
 projects/test_dht11/Makefile                  | 147 ++++
 projects/test_dht11/lib/adc.c                 |  46 ++
 projects/test_dht11/lib/adc.h                 |  19 +
 .../{onewire_test => test_dht11}/lib/calc.h   |   0
 projects/test_dht11/lib/color.c               |  95 +++
 projects/test_dht11/lib/color.h               |  55 ++
 projects/test_dht11/lib/debounce.c            |  45 ++
 projects/test_dht11/lib/debounce.h            |  64 ++
 projects/test_dht11/lib/dht11.c               |  88 +++
 projects/test_dht11/lib/dht11.h               |  16 +
 .../{onewire_test => test_dht11}/lib/iopins.c |   0
 .../{onewire_test => test_dht11}/lib/iopins.h |   0
 projects/test_dht11/lib/lcd.c                 | 358 +++++++++
 projects/test_dht11/lib/lcd.h                 | 146 ++++
 projects/test_dht11/lib/nsdelay.h             |  21 +
 projects/test_dht11/lib/onewire.c             | 225 ++++++
 projects/test_dht11/lib/onewire.h             |  51 ++
 projects/test_dht11/lib/sonar.c               | 139 ++++
 projects/test_dht11/lib/sonar.h               |  66 ++
 .../{onewire_test => test_dht11}/lib/stream.c |   0
 .../{onewire_test => test_dht11}/lib/stream.h |   0
 .../{onewire_test => test_dht11}/lib/uart.c   |   0
 .../{onewire_test => test_dht11}/lib/uart.h   |   0
 projects/test_dht11/lib/wsrgb.c               | 132 ++++
 projects/test_dht11/lib/wsrgb.h               |  51 ++
 projects/test_dht11/main.c                    |  36 +
 .../{onewire_test => test_onewire}/Makefile   |   0
 projects/test_onewire/lib/calc.h              |  87 +++
 projects/test_onewire/lib/iopins.c            | 276 +++++++
 projects/test_onewire/lib/iopins.h            | 213 ++++++
 .../lib/onewire.h                             |   0
 projects/test_onewire/lib/stream.c            | 213 ++++++
 projects/test_onewire/lib/stream.h            | 100 +++
 projects/test_onewire/lib/uart.c              | 678 ++++++++++++++++++
 projects/test_onewire/lib/uart.h              | 253 +++++++
 .../{onewire_test => test_onewire}/main.c     |   0
 36 files changed, 3620 insertions(+)
 create mode 100644 projects/test_dht11/Makefile
 create mode 100644 projects/test_dht11/lib/adc.c
 create mode 100644 projects/test_dht11/lib/adc.h
 rename projects/{onewire_test => test_dht11}/lib/calc.h (100%)
 create mode 100644 projects/test_dht11/lib/color.c
 create mode 100644 projects/test_dht11/lib/color.h
 create mode 100644 projects/test_dht11/lib/debounce.c
 create mode 100644 projects/test_dht11/lib/debounce.h
 create mode 100644 projects/test_dht11/lib/dht11.c
 create mode 100644 projects/test_dht11/lib/dht11.h
 rename projects/{onewire_test => test_dht11}/lib/iopins.c (100%)
 rename projects/{onewire_test => test_dht11}/lib/iopins.h (100%)
 create mode 100644 projects/test_dht11/lib/lcd.c
 create mode 100644 projects/test_dht11/lib/lcd.h
 create mode 100644 projects/test_dht11/lib/nsdelay.h
 create mode 100644 projects/test_dht11/lib/onewire.c
 create mode 100644 projects/test_dht11/lib/onewire.h
 create mode 100644 projects/test_dht11/lib/sonar.c
 create mode 100644 projects/test_dht11/lib/sonar.h
 rename projects/{onewire_test => test_dht11}/lib/stream.c (100%)
 rename projects/{onewire_test => test_dht11}/lib/stream.h (100%)
 rename projects/{onewire_test => test_dht11}/lib/uart.c (100%)
 rename projects/{onewire_test => test_dht11}/lib/uart.h (100%)
 create mode 100644 projects/test_dht11/lib/wsrgb.c
 create mode 100644 projects/test_dht11/lib/wsrgb.h
 create mode 100644 projects/test_dht11/main.c
 rename projects/{onewire_test => test_onewire}/Makefile (100%)
 create mode 100644 projects/test_onewire/lib/calc.h
 create mode 100644 projects/test_onewire/lib/iopins.c
 create mode 100644 projects/test_onewire/lib/iopins.h
 rename projects/{onewire_test => test_onewire}/lib/onewire.h (100%)
 create mode 100644 projects/test_onewire/lib/stream.c
 create mode 100644 projects/test_onewire/lib/stream.h
 create mode 100644 projects/test_onewire/lib/uart.c
 create mode 100644 projects/test_onewire/lib/uart.h
 rename projects/{onewire_test => test_onewire}/main.c (100%)

diff --git a/projects/test_dht11/Makefile b/projects/test_dht11/Makefile
new file mode 100644
index 0000000..6ed6ce1
--- /dev/null
+++ b/projects/test_dht11/Makefile
@@ -0,0 +1,147 @@
+## === CPU settings ===
+# CPU type
+MCU	= atmega328p
+# CPU frequency
+F_CPU = 16000000
+# Fuses
+LFUSE = 0xFF
+HFUSE = 0xDE
+EFUSE = 0x05
+
+
+## === Source files ===
+# Main C file
+MAIN = main.c
+# Extra C files in this folder
+LOCAL_SOURCE =
+
+# Library directory (with C files)
+EXTRA_SOURCE_DIR = lib/
+
+
+# C files in the library directory
+EXTRA_SOURCE_FILES = uart.c iopins.c stream.c adc.c dht11.c sonar.c onewire.c
+#Files that need config file:
+# EXTRA_SOURCE_FILES += lcd.c
+# EXTRA_SOURCE_FILES += color.c wsrgb.c
+# EXTRA_SOURCE_FILES += debouce.c
+
+
+## === Programmer ===
+PROGRAMMER_TYPE = arduino
+PROGRAMMER_ARGS = -b 57600 -P /dev/ttyUSB0
+
+
+## === C flags ===
+
+CFLAGS = -std=gnu99 -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -I. -I$(EXTRA_SOURCE_DIR)
+CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
+CFLAGS += -Wall -Wno-main -Wno-strict-prototypes -Wno-comment
+CFLAGS += -g2 -Wextra -Wfatal-errors -Wno-unused-but-set-variable
+CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
+# CFLAGS += -lm  ## Math
+# CFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm  ## for floating-point printf
+# CFLAGS += -Wl,-u,vfprintf -lprintf_min      ## for smaller printf
+CFLAGS_BUILD = $(CFLAGS) -Os
+
+
+# ---------------------------------------------------------------------------
+
+## Defined programs / locations
+CC = avr-gcc
+OBJCOPY = avr-objcopy
+OBJDUMP = avr-objdump
+AVRSIZE = avr-size
+AVRDUDE = avrdude
+
+## === File lists ===
+TARGET = $(strip $(basename $(MAIN)))
+SRC1 = $(TARGET).c
+SRC = $(SRC1)
+EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
+SRC += $(EXTRA_SOURCE)
+SRC += $(LOCAL_SOURCE)
+
+HEADERS = $(SRC:.c=.h)
+OBJ = $(SRC:.c=.o)
+
+
+## === File generation ===
+all: $(TARGET).hex size
+pre: $(TARGET).pre
+
+%.hex: %.elf
+	$(OBJCOPY) -R .eeprom -O ihex $< $@
+
+%.elf: $(SRC)
+	$(CC) $(CFLAGS_BUILD) $(SRC) --output $@
+
+%.pre: $(SRC1)
+	$(CC) $(CFLAGS) -E $(SRC1) --output $@
+
+%.eeprom: %.elf
+	$(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
+
+%.lst: %.elf
+	$(OBJDUMP) -S $< > $@
+
+# Show debug info
+debug:
+	@echo
+	@echo "Source files:"   $(SRC)
+	@echo "MCU, F_CPU, BAUD:"  $(MCU), $(F_CPU), $(BAUD)
+	@echo
+
+
+# Disassemble the ELF
+disassemble: $(TARGET).lst
+dis: disassemble
+lst: disassemble
+
+# Make eeprom file
+eeprom: $(TARGET).eeprom
+
+# Show how big the resulting program is
+size:  $(TARGET).elf
+	$(AVRSIZE) -C --mcu=$(MCU) $(TARGET).elf
+
+# Clean all produced trash
+clean:
+	rm -f $(TARGET).elf $(TARGET).hex $(TARGET).obj \
+	$(TARGET).o $(TARGET).d $(TARGET).eep $(TARGET).lst \
+	$(TARGET).lss $(TARGET).sym $(TARGET).map $(TARGET)~ \
+	$(TARGET).eeprom
+
+# Clean all trash
+purge:
+	rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~
+
+
+## === avrdude ===
+
+flash: $(TARGET).hex
+	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<
+
+flashe: $(TARGET).eeprom
+	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U eeprom:w:$<
+
+shell:
+	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nt
+
+fser: all flash ser
+
+ser:
+	gtkterm -p /dev/ttyUSB0
+
+# === fuses ===
+
+FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
+
+fuses:
+	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) \
+	           $(PROGRAMMER_ARGS) $(FUSE_STRING)
+show_fuses:
+	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -nv
+
+set_default_fuses:  FUSE_STRING = -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m -U efuse:w:$(EFUSE):m
+set_default_fuses:  fuses
diff --git a/projects/test_dht11/lib/adc.c b/projects/test_dht11/lib/adc.c
new file mode 100644
index 0000000..33938be
--- /dev/null
+++ b/projects/test_dht11/lib/adc.c
@@ -0,0 +1,46 @@
+#include <avr/io.h>
+#include <stdbool.h>
+
+#include "calc.h"
+#include "adc.h"
+
+/** Initialize the ADC */
+void adc_init()
+{
+	ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);  // 128 prescaler -> 125 kHz
+	ADMUX  |= _BV(REFS0);  // Voltage reference
+	sbi(ADCSRA, ADEN);  // Enable ADC
+}
+
+
+/** Disable AD */
+void adc_disable()
+{
+	cbi(ADCSRA, ADEN);
+}
+
+
+/** Sample analog pin with 8-bit precision */
+uint8_t adc_read_byte(uint8_t channel)
+{
+	set_low_nibble(ADMUX, channel);  // Select channel to sample
+	sbi(ADMUX, ADLAR);  // Align result to left
+	sbi(ADCSRA, ADSC);  // Start conversion
+
+	while(bit_is_high(ADCSRA, ADSC));  // Wait for it...
+
+	return ADCH;  // The upper 8 bits of ADC result
+}
+
+
+/** Sample analog pin with 10-bit precision */
+uint16_t adc_read_word(uint8_t channel)
+{
+	set_low_nibble(ADMUX, channel);  // Select channel to sample
+	cbi(ADMUX, ADLAR);  // Align result to right
+	sbi(ADCSRA, ADSC);  // Start conversion
+
+	while(get_bit(ADCSRA, ADSC));  // Wait for it...
+
+	return ADCW;  // The whole ADC word (10 bits)
+}
diff --git a/projects/test_dht11/lib/adc.h b/projects/test_dht11/lib/adc.h
new file mode 100644
index 0000000..fdd08d2
--- /dev/null
+++ b/projects/test_dht11/lib/adc.h
@@ -0,0 +1,19 @@
+#pragma once
+
+//
+// Utilities for build-in A/D converter
+//
+
+#include <avr/io.h>
+
+/** Initialize the ADC */
+void adc_init();
+
+/** Disable AD (for power saving?) */
+void adc_disable();
+
+/** Sample analog pin with 8-bit precision */
+uint8_t adc_read_byte(uint8_t channel);
+
+/** Sample analog pin with 10-bit precision */
+uint16_t adc_read_word(uint8_t channel);
diff --git a/projects/onewire_test/lib/calc.h b/projects/test_dht11/lib/calc.h
similarity index 100%
rename from projects/onewire_test/lib/calc.h
rename to projects/test_dht11/lib/calc.h
diff --git a/projects/test_dht11/lib/color.c b/projects/test_dht11/lib/color.c
new file mode 100644
index 0000000..101613e
--- /dev/null
+++ b/projects/test_dht11/lib/color.c
@@ -0,0 +1,95 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <stdint.h>
+
+#include "iopins.h"
+#include "nsdelay.h"
+#include "color.h"
+
+
+// --- HSL ---
+
+#ifdef HSL_LINEAR
+	const uint8_t FADE_128[] = {
+		0,	1,	1,	1,	1,	1,	2,	2,	2,	2,	2,	3,	3,	3,	4,	4,	4,	4,
+		5,	5,	6,	6,	6,	7,	7,	8,	8,	8,	9,	10,	10,	10,	11,	12,	13,	14,
+		14,	15,	16,	17,	18,	20,	21,	22,	24,	26,	27,	28,	30,	31,	32,	34,	35,	36,
+		38,	39,	40,	41,	42,	44,	45,	46,	48,	49,	50,	52,	54,	56,	58,	59,	61,	63,
+		65,	67,	68,	69,	71,	72,	74,	76,	78,	80,	82,	85,	88,	90,	92,	95,	98,	100,
+		103, 106, 109, 112, 116, 119, 122, 125, 129, 134, 138, 142,	147, 151,
+		153, 156, 160, 163, 165, 170, 175, 180, 185, 190, 195, 200, 207, 214, 218,
+		221, 225, 228, 232, 234, 241, 248, 254, 255
+	};
+#endif
+
+// based on: https://github.com/lewisd32/avr-hsl2rgb
+xrgb_t hsl_xrgb(const hsl_t cc)
+{
+	// 0 .. 256*3
+	const uint16_t hh = (uint16_t) cc.h * 3;
+	const uint8_t hue_mod = hh % 256;
+
+	uint8_t r_temp, g_temp, b_temp;
+	if (hh < 256) {
+		r_temp = hue_mod ^ 255;
+		g_temp = hue_mod;
+		b_temp = 0;
+	} else if (hh < 512) {
+		r_temp = 0;
+		g_temp = hue_mod ^ 255;
+		b_temp = hue_mod;
+	} else if (hh < 768) {
+		r_temp = hue_mod;
+		g_temp = 0;
+		b_temp = hue_mod ^ 255;
+	} else {
+		r_temp = 0;
+		g_temp = 0;
+		b_temp = 0;
+	}
+
+	const uint8_t inverse_sat = (cc.s ^ 255);
+
+	xrgb_t rgb;
+
+	uint8_t t8;
+	uint16_t t16;
+
+#ifdef HSL_LINEAR
+	const uint8_t bri = FADE_128[cc.l>>1];
+#else
+	const uint8_t bri = cc.l;
+#endif
+
+	t8 = r_temp;
+	t16 = t8 * cc.s + t8;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	t8 = t8 + inverse_sat;
+	t16 = t8 * bri;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	rgb.r = t8;
+
+	t8 = g_temp;
+	t16 = t8 * cc.s;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	t8 = t8 + inverse_sat;
+	t16 = t8 * bri;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	rgb.g = t8;
+
+	t8 = b_temp;
+	t16 = t8 * cc.s;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	t8 = t8 + inverse_sat;
+	t16 = t8 * bri;
+	t16 = t16 + t8;
+	t8 = t16 >> 8;
+	rgb.b = t8;
+
+	return rgb;
+}
diff --git a/projects/test_dht11/lib/color.h b/projects/test_dht11/lib/color.h
new file mode 100644
index 0000000..716f738
--- /dev/null
+++ b/projects/test_dht11/lib/color.h
@@ -0,0 +1,55 @@
+#pragma once
+
+// --- color types ---
+//
+// The XXXc macros don't use cast, so they can be used in array initializers.
+//
+//  xrgb ... 3-byte true-color RGB (8 bits per component)
+//  rgb24 ... 24-bit color value, with equal nr of bits per component
+//
+//  XX_r (_g, _b) ... extract component from the color, and convert it to 0..255
+
+// Define HSL_LINEAR to get more linear brightness in hsl->rgb conversion
+
+
+typedef struct {
+	uint8_t r;
+	uint8_t g;
+	uint8_t b;
+} xrgb_t;
+
+
+typedef uint32_t rgb24_t;
+
+#define xrgb(rr, gg, bb) ((xrgb_t)xrgbc(rr, gg, bb))
+// xrgb for constant array declarations
+#define xrgbc(rr, gg, bb) { .r = ((uint8_t)(rr)), .g = ((uint8_t)(gg)), .b = ((uint8_t)(bb)) }
+#define xrgb_r(c) ((uint8_t)(c.r))
+#define xrgb_g(c) ((uint8_t)(c.g))
+#define xrgb_b(c) ((uint8_t)(c.b))
+#define xrgb_rgb24(c) ((((rgb24_t)c.r) << 16) | (((rgb24_t)c.g) << 8) | (((rgb24_t)c.b)))
+#define xrgb_rgb15(c) (((((rgb15_t)c.r) & 0xF8) << 7) | ((((rgb15_t)c.g) & 0xF8) << 2) | ((((rgb15_t)c.b) & 0xF8) >> 3))
+#define xrgb_rgb12(c) (((((rgb12_t)c.r) & 0xF0) << 4) | ((((rgb12_t)c.g) & 0xF0)) | ((((rgb12_t)c.b) & 0xF0) >> 4))
+#define xrgb_rgb6(c)  (((((rgb6_t)c.r) & 0xC0) >> 2)  | ((((rgb6_t)c.g) & 0xC0) >> 4) | ((((rgb6_t)c.b) & 0xC0) >> 6))
+
+#define rgb24c(r,g,b) (((((rgb24_t)r) & 0xFF) << 16) | ((((rgb24_t)g) & 0xFF) << 8) | (((rgb24_t)b) & 0xFF))
+#define rgb24(r,g,b) ((rgb24_t) rgb24(r,g,b))
+
+#define rgb24_r(c) ((((rgb24_t) (c)) >> 16) & 0xFF)
+#define rgb24_g(c) ((((rgb24_t) (c)) >> 8) & 0xFF)
+#define rgb24_b(c) ((((rgb24_t) (c)) >> 0) & 0xFF)
+#define rgb24_xrgb(c) xrgb(rgb24_r(c), rgb24_g(c), rgb24_b(c))
+#define rgb24_xrgbc(c) xrgbc(rgb24_r(c), rgb24_g(c), rgb24_b(c))
+
+#define add_xrgb(x, y) ((xrgb_t) { (((y).r > (255 - (x).r)) ? 255 : ((x).r + (y).r)), (((y).g > (255 - (x).g)) ? 255 : ((x).g + (y).g)), (((y).b > 255 - (x).b) ? 255 : ((x).b + (y).b)) })
+
+
+// HSL data structure
+typedef struct {
+	uint8_t h;
+	uint8_t s;
+	uint8_t l;
+} hsl_t;
+
+/* Convert HSL to XRGB */
+xrgb_t hsl_xrgb(const hsl_t color);
diff --git a/projects/test_dht11/lib/debounce.c b/projects/test_dht11/lib/debounce.c
new file mode 100644
index 0000000..0e6e803
--- /dev/null
+++ b/projects/test_dht11/lib/debounce.c
@@ -0,0 +1,45 @@
+#include <avr/io.h>
+#include <stdbool.h>
+
+#include "debounce.h"
+#include "calc.h"
+#include "iopins.h"
+#include "debo_config.h"
+
+/** Debounce data array */
+uint8_t debo_next_slot = 0;
+
+uint8_t debo_register(PORT_P reg, uint8_t bit, bool invert)
+{
+	debo_slots[debo_next_slot] = (debo_slot_t){
+		.reg = reg,
+		.bit = bit | ((invert & 1) << 7) | (get_bit_p(reg, bit) << 6), // bit 7 = invert, bit 6 = state
+		.count = 0,
+	};
+
+	return debo_next_slot++;
+}
+
+
+/** Check debounced pins, should be called periodically. */
+void debo_tick()
+{
+	for (uint8_t i = 0; i < debo_next_slot; i++) {
+		// current pin value (right 3 bits, xored with inverse bit)
+		bool value = get_bit_p(debo_slots[i].reg, debo_slots[i].bit & 0x7);
+
+		if (value != get_bit(debo_slots[i].bit, 6)) {
+
+			// different pin state than last recorded state
+			if (debo_slots[i].count < DEBO_TICKS) {
+				debo_slots[i].count++;
+			} else {
+				// overflown -> latch value
+				set_bit(debo_slots[i].bit, 6, value); // set state bit
+				debo_slots[i].count = 0;
+			}
+		} else {
+			debo_slots[i].count = 0; // reset the counter
+		}
+	}
+}
diff --git a/projects/test_dht11/lib/debounce.h b/projects/test_dht11/lib/debounce.h
new file mode 100644
index 0000000..918111d
--- /dev/null
+++ b/projects/test_dht11/lib/debounce.h
@@ -0,0 +1,64 @@
+#pragma once
+
+//
+//  An implementation of button debouncer.
+//
+//  ----
+//
+//  You must provide a config file debo_config.h (next to your main.c)
+//
+//  A pin is registered like this:
+//
+//    #define BTN1 12 // pin D12
+//    #define BTN2 13
+//
+//    debo_add(BTN0);  // The function returns number assigned to the pin (0, 1, ...)
+//    debo_add_rev(BTN1);  // active low
+//    debo_register(&PINB, PB2, 0);  // direct access - register, pin & invert
+//
+//  Then periodically call the tick function (perhaps in a timer interrupt):
+//
+//    debo_tick();
+//
+//  To check if input is active, use
+//
+//    debo_get_pin(0); // state of input #0 (registered first)
+//    debo_get_pin(1); // state of input #1 (registered second)
+//
+
+
+#include <avr/io.h>
+#include <stdbool.h>
+
+#include "calc.h"
+#include "pins.h"
+
+// Your config file
+#include "debo_config.h"
+/*
+	#define DEBO_CHANNELS 2
+	#define DDEBO_TICKS 5
+*/
+
+
+/* Internal deboucer entry */
+typedef struct {
+	PORT_P reg;    // pointer to IO register
+	uint8_t bit;   // bits 6 and 7 of this hold "state" & "invert" flag
+	uint8_t count; // number of ticks this was in the new state
+} debo_slot_t;
+
+debo_slot_t debo_slots[DEBO_CHANNELS];
+
+/** Add a pin for debouncing (must be used with constant args) */
+#define debo_add_rev(pin) debo_register(&_pin(pin), _pn(pin), 1)
+#define debo_add(pin)  debo_register(&_pin(pin), _pn(pin), 0)
+
+/** Add a pin for debouncing (low level function) */
+uint8_t debo_register(PORT_P pin_reg_pointer, uint8_t bit, bool invert);
+
+/** Check debounced pins, should be called periodically. */
+void debo_tick();
+
+/** Get a value of debounced pin */
+#define debo_get_pin(i) (get_bit(debo_slots[i].bit, 6) ^ get_bit(debo_slots[i].bit, 7))
diff --git a/projects/test_dht11/lib/dht11.c b/projects/test_dht11/lib/dht11.c
new file mode 100644
index 0000000..70b57f1
--- /dev/null
+++ b/projects/test_dht11/lib/dht11.c
@@ -0,0 +1,88 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "iopins.h"
+#include "dht11.h"
+
+/** Read one bit */
+bool _dht11_rxbit(const uint8_t pin)
+{
+	// Wait until start of pulse
+	while (is_low_n(pin));
+
+	uint8_t cnt = 0;
+	while (is_high_n(pin))
+	{
+		cnt++;
+		_delay_us(5);
+	}
+
+	return (cnt > 8);
+}
+
+
+/** Read one byte */
+uint8_t _dht11_rxbyte(const uint8_t pin)
+{
+	uint8_t byte = 0;
+
+	for (uint8_t i = 0; i < 8; i++)
+	{
+		if (_dht11_rxbit(pin))
+			byte |= (1 << (7 - i));
+	}
+
+	return byte;
+}
+
+
+/** Read tehmperature and humidity from the DHT11, returns false on failure */
+bool dht11_read(const uint8_t pin, dht11_result_t* result)
+{
+	// bus down for > 18 ms
+	as_output_n(pin);
+	pin_low_n(pin);
+	_delay_ms(20);
+
+	// bus up for 20-40us
+	pin_high_n(pin);
+	_delay_us(20);
+
+	// release
+	as_input_pu_n(pin);
+
+	// DHT should send 80us LOW & 80us HIGH
+
+	_delay_us(40);
+	if (!is_low_n(pin))
+		return false; // init error
+
+	_delay_us(80);
+	if (!is_high_n(pin))
+		return false; // init error
+
+	// skip to start of first bit
+	_delay_us(50);
+
+	// Receive 5 data bytes (Rh int, Rh dec, Temp int, Temp dec, Checksum)
+	// Decimal bytes are zero for DHT11 -> we can ignore them.
+	uint8_t bytes[5];
+	uint8_t sum = 0;
+
+	for (uint8_t i = 0; i < 5; i++)
+	{
+		uint8_t b = _dht11_rxbyte(pin);
+		bytes[i] = b;
+		if (i < 4) sum += b;
+	}
+
+	// Verify checksum
+	if (sum != bytes[4]) return false;
+
+	result->rh = bytes[0];
+	result->temp = bytes[2];
+
+	return true;
+}
diff --git a/projects/test_dht11/lib/dht11.h b/projects/test_dht11/lib/dht11.h
new file mode 100644
index 0000000..53300e3
--- /dev/null
+++ b/projects/test_dht11/lib/dht11.h
@@ -0,0 +1,16 @@
+#pragma once
+
+//
+// Reading temperature and relative humidity from DHT11
+//
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct {
+	int8_t temp;
+	int8_t rh;
+} dht11_result_t;
+
+/** Read tehmperature and humidity from the DHT11, returns false on failure */
+bool dht11_read(const uint8_t pin, dht11_result_t* result);
diff --git a/projects/onewire_test/lib/iopins.c b/projects/test_dht11/lib/iopins.c
similarity index 100%
rename from projects/onewire_test/lib/iopins.c
rename to projects/test_dht11/lib/iopins.c
diff --git a/projects/onewire_test/lib/iopins.h b/projects/test_dht11/lib/iopins.h
similarity index 100%
rename from projects/onewire_test/lib/iopins.h
rename to projects/test_dht11/lib/iopins.h
diff --git a/projects/test_dht11/lib/lcd.c b/projects/test_dht11/lib/lcd.c
new file mode 100644
index 0000000..a9ceb13
--- /dev/null
+++ b/projects/test_dht11/lib/lcd.c
@@ -0,0 +1,358 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+
+#include "calc.h"
+#include "iopins.h"
+#include "nsdelay.h"
+#include "lcd.h"
+#include "lcd_config.h"
+
+// Start address of rows
+const uint8_t LCD_ROW_ADDR[] = {0x00, 0x40, 0x14, 0x54};
+
+
+// Shared stream instance
+static STREAM _lcd_singleton;
+STREAM* lcd;
+
+
+// Internal prototypes
+void _lcd_mode_r();
+void _lcd_mode_w();
+void _lcd_clk();
+void _lcd_wait_bf();
+void _lcd_write_byte(uint8_t bb);
+uint8_t _lcd_read_byte();
+
+
+// Write utilities
+#define _lcd_write_low(bb)  _lcd_write_nibble((bb) & 0x0F)
+#define _lcd_write_high(bb) _lcd_write_nibble(((bb) & 0xF0) >> 4)
+#define _lcd_write_nibble(nib) do {                \
+	set_pin(LCD_D7, get_bit((nib), 3));     \
+	set_pin(LCD_D6, get_bit((nib), 2));     \
+	set_pin(LCD_D5, get_bit((nib), 1));     \
+	set_pin(LCD_D4, get_bit((nib), 0));     \
+} while(0)
+
+
+// 0 W, 1 R
+bool _lcd_mode;
+
+struct {
+	uint8_t x;
+	uint8_t y;
+} _pos;
+
+enum {
+	TEXT = 0,
+	CG = 1
+} _addrtype;
+
+
+/** Initialize the display */
+void lcd_init()
+{
+	// configure pins as output
+	as_output(LCD_E);
+	as_output(LCD_RW);
+	as_output(LCD_RS);
+	_lcd_mode = 1;  // force data pins to output
+	_lcd_mode_w();
+
+	// Magic sequence to invoke Cthulhu (or enter 4-bit mode)
+	_delay_ms(16);
+	_lcd_write_nibble(0b0011);
+	_lcd_clk();
+	_delay_ms(5);
+	_lcd_clk();
+	_delay_ms(5);
+	_lcd_clk();
+	_delay_ms(5);
+	_lcd_write_nibble(0b0010);
+	_lcd_clk();
+	_delay_us(100);
+
+	// Configure the display
+	lcd_command(LCD_IFACE_4BIT_2LINE);
+	lcd_command(LCD_DISABLE);
+	lcd_command(LCD_CLEAR);
+	lcd_command(LCD_MODE_INC);
+
+	// mark as enabled
+	lcd_enable();
+
+	_lcd_singleton.tx = &lcd_write;
+	_lcd_singleton.rx = &lcd_read;
+
+	// Stream
+	lcd = &_lcd_singleton;
+
+	_pos.x = 0;
+	_pos.y = 0;
+	_addrtype = TEXT;
+}
+
+
+/** Send a pulse on the ENABLE line */
+void _lcd_clk()
+{
+	pin_high(LCD_E);
+	delay_ns(450);
+	pin_low(LCD_E);
+}
+
+
+/** Enter READ mode */
+void _lcd_mode_r()
+{
+	if (_lcd_mode == 1) return;  // already in R mode
+
+	pin_high(LCD_RW);
+
+	as_input_pu(LCD_D7);
+	as_input_pu(LCD_D6);
+	as_input_pu(LCD_D5);
+	as_input_pu(LCD_D4);
+
+	_lcd_mode = 1;
+}
+
+
+/** Enter WRITE mode */
+void _lcd_mode_w()
+{
+	if (_lcd_mode == 0) return;  // already in W mode
+
+	pin_low(LCD_RW);
+
+	as_output(LCD_D7);
+	as_output(LCD_D6);
+	as_output(LCD_D5);
+	as_output(LCD_D4);
+
+	_lcd_mode = 0;
+}
+
+
+/** Read a byte */
+uint8_t _lcd_read_byte()
+{
+	_lcd_mode_r();
+
+	uint8_t res = 0;
+
+	_lcd_clk();
+	res = (get_pin(LCD_D7) << 7) | (get_pin(LCD_D6) << 6) | (get_pin(LCD_D5) << 5) | (get_pin(LCD_D4) << 4);
+
+	_lcd_clk();
+	res |= (get_pin(LCD_D7) << 3) | (get_pin(LCD_D6) << 2) | (get_pin(LCD_D5) << 1) | (get_pin(LCD_D4) << 0);
+
+	return res;
+}
+
+
+/** Write an instruction byte */
+void lcd_command(uint8_t bb)
+{
+	_lcd_wait_bf();
+	pin_low(LCD_RS);  // select instruction register
+	_lcd_write_byte(bb);    // send instruction byte
+}
+
+
+/** Write a data byte */
+void lcd_write(uint8_t bb)
+{
+	if (_addrtype == TEXT) {
+		if (bb == '\r') {
+			// CR
+			_pos.x = 0;
+			lcd_xy(_pos.x, _pos.y);
+			return;
+		}
+
+		if (bb == '\n') {
+			// LF
+			_pos.y++;
+			lcd_xy(_pos.x, _pos.y);
+			return;
+		}
+
+		_pos.x++;
+	}
+
+	_lcd_wait_bf();
+	pin_high(LCD_RS);  // select data register
+	_lcd_write_byte(bb);  // send data byte
+}
+
+
+/** Read BF & Address */
+uint8_t lcd_read_bf_addr()
+{
+	pin_low(LCD_RS);
+	return _lcd_read_byte();
+}
+
+
+/** Read CGRAM or DDRAM */
+uint8_t lcd_read()
+{
+	if (_addrtype == TEXT) _pos.x++;
+
+	pin_high(LCD_RS);
+	return _lcd_read_byte();
+}
+
+
+/** Write a byte using the 4-bit interface */
+void _lcd_write_byte(uint8_t bb)
+{
+	_lcd_mode_w();  // enter W mode
+
+	_lcd_write_high(bb);
+	_lcd_clk();
+
+	_lcd_write_low(bb);
+	_lcd_clk();
+}
+
+
+
+/** Wait until the device is ready */
+void _lcd_wait_bf()
+{
+	uint8_t d = 0;
+	while(d++ < 120 && lcd_read_bf_addr() & _BV(7))
+		_delay_us(1);
+}
+
+
+/** Send a string to LCD */
+void lcd_puts(char* str_p)
+{
+	char c;
+	while ((c = *str_p++))
+		lcd_putc(c);
+}
+
+
+/** Print from progmem */
+void lcd_puts_P(const char* str_p)
+{
+	char c;
+	while ((c = pgm_read_byte(str_p++)))
+		lcd_putc(c);
+}
+
+
+/** Sedn a char to LCD */
+void lcd_putc(const char c)
+{
+	lcd_write(c);
+}
+
+
+/** Set cursor position */
+void lcd_xy(const uint8_t x, const uint8_t y)
+{
+	_pos.x = x;
+	_pos.y = y;
+	lcd_addr(LCD_ROW_ADDR[y] + (x));
+}
+
+
+uint8_t _lcd_old_cursor = CURSOR_NONE;
+bool _lcd_enabled = false;
+
+/** Set LCD cursor. If not enabled, only remember it. */
+void lcd_cursor(uint8_t type)
+{
+	_lcd_old_cursor = (type & CURSOR_BOTH);
+
+	if (_lcd_enabled) lcd_command(LCD_CURSOR_NONE | _lcd_old_cursor);
+}
+
+
+/** Display display (preserving cursor) */
+void lcd_disable()
+{
+	lcd_command(LCD_DISABLE);
+	_lcd_enabled = false;
+}
+
+
+/** Enable display (restoring cursor) */
+void lcd_enable()
+{
+	_lcd_enabled = true;
+	lcd_cursor(_lcd_old_cursor);
+}
+
+
+/** Go home */
+void lcd_home()
+{
+	lcd_command(LCD_HOME);
+	_pos.x = 0;
+	_pos.y = 0;
+	_addrtype = TEXT;
+}
+
+
+/** Clear the screen */
+void lcd_clear()
+{
+	lcd_command(LCD_CLEAR);
+	_pos.x = 0;
+	_pos.y = 0;
+	_addrtype = TEXT;
+}
+
+
+/** Define a glyph */
+void lcd_glyph(const uint8_t index, const uint8_t* array)
+{
+	lcd_addr_cg(index * 8);
+	for (uint8_t i = 0; i < 8; ++i)	{
+		lcd_write(array[i]);
+	}
+
+	// restore previous position
+	lcd_xy(_pos.x, _pos.y);
+	_addrtype = TEXT;
+}
+
+
+/** Define a glyph */
+void lcd_glyph_P(const uint8_t index, const uint8_t* array)
+{
+	lcd_addr_cg(index * 8);
+	for (uint8_t i = 0; i < 8; ++i)	{
+		lcd_write(pgm_read_byte(&array[i]));
+	}
+
+	// restore previous position
+	lcd_xy(_pos.x, _pos.y);
+	_addrtype = TEXT;
+}
+
+
+/** Set address in CGRAM */
+void lcd_addr_cg(const uint8_t acg)
+{
+	_addrtype = CG;
+	lcd_command(0b01000000 | ((acg) & 0b00111111));
+}
+
+
+/** Set address in DDRAM */
+void lcd_addr(const uint8_t add)
+{
+	_addrtype = TEXT;
+	lcd_command(0b10000000 | ((add) & 0b01111111));
+}
diff --git a/projects/test_dht11/lib/lcd.h b/projects/test_dht11/lib/lcd.h
new file mode 100644
index 0000000..7f48d16
--- /dev/null
+++ b/projects/test_dht11/lib/lcd.h
@@ -0,0 +1,146 @@
+#pragma once
+
+//  HD44780 LCD display driver - 4-bit mode
+//
+//  LCD pins are configured using a file lcd_config.h, which you
+//  have to add next to your main.c file.
+//
+//  Content can be something like this:
+//
+//
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "stream.h"
+
+// Your file with configs
+#include "lcd_config.h"
+/*
+	#define LCD_RS 10
+	#define LCD_RW 11
+	#define LCD_E  12
+	#define LCD_D4 13
+	#define LCD_D5 14
+	#define LCD_D6 15
+	#define LCD_D7 16
+*/
+
+
+
+// Shared LCD stream object
+// Can be used with functions from stream.h once LCD is initialized
+extern STREAM* lcd;
+
+
+// --- Commands ---
+
+// Clear screen (reset)
+#define LCD_CLEAR 0b00000001
+// Move cursor to (0,0), unshift...
+#define LCD_HOME  0b00000010
+
+// Set mode: Increment + NoShift
+#define LCD_MODE_INC         0b00000110
+// Set mode: Increment + Shift
+#define LCD_MODE_INC_SHIFT   0b00000111
+
+// Set mode: Decrement + NoShift
+#define LCD_MODE_DEC         0b00000100
+// Set mode: Decrement + Shift
+#define LCD_MODE_DEC_SHIFT   0b00000101
+
+// Disable display (data remains untouched)
+#define LCD_DISABLE          0b00001000
+
+// Disable cursor
+#define LCD_CURSOR_NONE      0b00001100
+// Set cursor to still underscore
+#define LCD_CURSOR_BAR       0b00001110
+// Set cursor to blinking block
+#define LCD_CURSOR_BLINK     0b00001101
+// Set cursor to both of the above at once
+#define LCD_CURSOR_BOTH      (LCD_CURSOR_BAR | LCD_CURSOR_BLINK)
+
+// Move cursor
+#define LCD_MOVE_LEFT  0b00010000
+#define LCD_MOVE_RIGHT 0b00010100
+
+// Shift display
+#define LCD_SHIFT_LEFT  0b00011000
+#define LCD_SHIFT_RIGHT 0b00011100
+
+// Set iface to 5x7 font, 1-line
+#define LCD_IFACE_4BIT_1LINE 0b00100000
+#define LCD_IFACE_8BIT_1LINE 0b00110000
+// Set iface to 5x7 font, 2-line
+#define LCD_IFACE_4BIT_2LINE 0b00101000
+#define LCD_IFACE_8BIT_2LINE 0b00111000
+
+
+/** Initialize the display */
+void lcd_init();
+
+/** Write an instruction byte */
+void lcd_command(uint8_t bb);
+
+/** Write a data byte */
+void lcd_write(uint8_t bb);
+
+/** Read BF & Address */
+uint8_t lcd_read_bf_addr();
+
+/** Read CGRAM or DDRAM */
+uint8_t lcd_read();
+
+/** Send a string to LCD */
+void lcd_puts(char* str_p);
+
+/** Send a string to LCD from program memory */
+void lcd_puts_P(const char* str_p);
+
+/** Sedn a char to LCD */
+void lcd_putc(const char c);
+
+/** Show string at X, Y */
+#define lcd_puts_xy(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts((str_p)); } while(0)
+
+/** Show string at X, Y */
+#define lcd_puts_xy_P(x, y, str_p) do { lcd_xy((x), (y)); lcd_puts_P((str_p)); } while(0)
+
+/** Show char at X, Y */
+#define lcd_putc_xy(x, y, c) do { lcd_xy((x), (y)); lcd_putc((c)); } while(0)
+
+/** Set cursor position */
+void lcd_xy(const uint8_t x, const uint8_t y);
+
+/** Set LCD cursor. If not enabled, only remember it. */
+#define CURSOR_NONE  0b00
+#define CURSOR_BAR   0b10
+#define CURSOR_BLINK 0b01
+#define CURSOR_BOTH  0b11
+void lcd_cursor(uint8_t type);
+
+/** Display display (preserving cursor) */
+void lcd_disable();
+
+/** Enable display (restoring cursor) */
+void lcd_enable();
+
+/** Go home */
+void lcd_home();
+
+/** Clear the screen */
+void lcd_clear();
+
+/** Define a glyph - 8 bytes, right 5 bits are used */
+void lcd_glyph(const uint8_t index, const uint8_t* array);
+
+/** Define a glyph that's in PROGMEM */
+void lcd_glyph_P(const uint8_t index, const uint8_t* array);
+
+/** Set address in CGRAM */
+void lcd_addr_cg(const uint8_t acg);
+
+/** Set address in DDRAM */
+void lcd_addr(const uint8_t add);
diff --git a/projects/test_dht11/lib/nsdelay.h b/projects/test_dht11/lib/nsdelay.h
new file mode 100644
index 0000000..dd93a83
--- /dev/null
+++ b/projects/test_dht11/lib/nsdelay.h
@@ -0,0 +1,21 @@
+#pragma once
+
+//
+// Functions for precise delays (nanoseconds / cycles)
+//
+
+#include <avr/io.h>
+#include <util/delay_basic.h>
+#include <stdint.h>
+
+/* Convert nanoseconds to cycle count */
+#define ns2cycles(ns)  ( (ns) / (1000000000L / (signed long) F_CPU) )
+
+/** Wait c cycles */
+#define delay_c(c)  (((c) > 0) ? __builtin_avr_delay_cycles(c) :  __builtin_avr_delay_cycles(0))
+
+/** Wait n nanoseconds, plus c cycles  */
+#define delay_ns_c(ns, c)  delay_c(ns2cycles(ns) + (c))
+
+/** Wait n nanoseconds  */
+#define delay_ns(ns)  delay_c(ns2cycles(ns))
diff --git a/projects/test_dht11/lib/onewire.c b/projects/test_dht11/lib/onewire.c
new file mode 100644
index 0000000..47748a1
--- /dev/null
+++ b/projects/test_dht11/lib/onewire.c
@@ -0,0 +1,225 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "iopins.h"
+#include "onewire.h"
+
+
+/** Perform bus reset. Returns true if any device is connected */
+bool ow_reset(const uint8_t pin)
+{
+	as_output_n(pin);
+	pin_low_n(pin);
+	_delay_us(480);
+
+	as_input_pu_n(pin);
+	_delay_us(70);
+
+	const bool a = get_pin_n(pin);
+
+	_delay_us(410);
+
+	return a;
+}
+
+
+/** Send a single bit */
+void _ow_tx_bit(const uint8_t pin, const bool bit)
+{
+	as_output_n(pin);
+	pin_low_n(pin);
+
+	if (bit) {
+		_delay_us(6);
+		as_input_pu_n(pin);
+		_delay_us(64);
+	} else {
+		_delay_us(60);
+		as_input_pu_n(pin);
+		_delay_us(10);
+	}
+}
+
+
+/** Send a single byte */
+void ow_send(const uint8_t pin, const uint8_t byte)
+{
+	for (uint8_t i = 0; i < 8; i++)
+	{
+		_ow_tx_bit(pin, (byte >> i) & 0x01);
+	}
+}
+
+
+/** Read a single bit */
+bool _ow_rx_bit(const uint8_t pin)
+{
+	as_output_n(pin);
+	pin_low_n(pin);
+	_delay_us(6);
+
+	as_input_pu_n(pin);
+	_delay_us(9);
+
+	const bool a = get_pin_n(pin);
+
+	_delay_us(55);
+
+	return a;
+}
+
+
+/** Read a single byte */
+uint8_t ow_read(const uint8_t pin)
+{
+	uint8_t byte = 0;
+
+	for (uint8_t i = 0; i < 8; i++)
+	{
+		byte = (byte >> 1) | (_ow_rx_bit(pin) << 7);
+	}
+
+	return byte;
+}
+
+
+/** Wait until the device is ready. Returns false on timeout */
+bool ow_wait_ready(const uint8_t pin)
+{
+	uint16_t timeout = 700;
+	as_input_pu_n(pin);
+
+	while (--timeout > 0)
+	{
+		if (is_high_n(pin)) return true;
+		_delay_ms(1);
+	}
+
+	return false;
+}
+
+
+/** Read bytes into an array */
+void ow_read_arr(const uint8_t pin, uint8_t* array, const uint8_t count)
+{
+	for (uint8_t i = 0; i < count; i++)
+	{
+		array[i] = ow_read(pin);
+	}
+}
+
+
+
+// ---------- CRC utils ----------
+
+/*
+ Dallas 1-wire CRC routines for Arduino with examples of usage.
+ The 16-bit routine is new.
+ The 8-bit routine is from http://github.com/paeaetech/paeae/tree/master/Libraries/ds2482/
+
+ Copyright (C) 2010 Kairama Inc
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+// Dallas 1-wire 16-bit CRC calculation. Developed from Maxim Application Note 27.
+
+/** Compute a CRC16 checksum */
+uint16_t crc16( uint8_t *data, uint8_t len)
+{
+	uint16_t crc = 0;
+
+	for (uint8_t i = 0; i < len; i++)
+	{
+		uint8_t inbyte = data[i];
+		for (uint8_t j = 0; j < 8; j++)
+		{
+			uint8_t mix = (crc ^ inbyte) & 0x01;
+			crc = crc >> 1;
+			if (mix)
+				crc = crc ^ 0xA001;
+
+			inbyte = inbyte >> 1;
+		}
+	}
+	return crc;
+}
+
+
+// The 1-Wire CRC scheme is described in Maxim Application Note 27:
+// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
+
+/** Compute a CRC8 checksum */
+uint8_t crc8(uint8_t *addr, uint8_t len)
+{
+	uint8_t crc = 0;
+
+	for (uint8_t i = 0; i < len; i++)
+	{
+		uint8_t inbyte = addr[i];
+		for (uint8_t j = 0; j < 8; j++)
+		{
+			uint8_t mix = (crc ^ inbyte) & 0x01;
+			crc >>= 1;
+			if (mix)
+					crc ^= 0x8C;
+
+			inbyte >>= 1;
+		}
+	}
+
+	return crc;
+}
+
+
+// --- utils for DS1820 ---
+
+
+/** Read temperature in 0.0625°C, or TEMP_ERROR on error */
+int16_t ds1820_read_temp(uint8_t pin)
+{
+	ow_send(pin, READ_SCRATCHPAD);
+	uint8_t bytes[9];
+	ow_read_arr(pin, bytes, 9);
+
+	uint8_t crc = crc8(bytes, 8);
+	if (crc != bytes[8]) {
+		return TEMP_ERROR;
+	} else {
+		int16_t a = ((bytes[1] << 8) | bytes[0]) >> 1;
+		a = a << 4;
+		a += (16 - bytes[6]) & 0x0F;
+		a -= 0x04;
+
+		return a;
+	}
+}
+
+/** Read temperature in 0.1°C, or TEMP_ERROR on error */
+int16_t ds1820_read_temp_c(uint8_t pin)
+{
+	int32_t temp = ds1820_read_temp(pin);
+
+	if (temp == TEMP_ERROR)
+		return TEMP_ERROR;
+
+	temp *= 625;
+	uint16_t rem = temp % 1000;
+	temp /= 1000;
+	if (rem >= 500) temp++;
+
+	return (int16_t) temp;
+}
+
diff --git a/projects/test_dht11/lib/onewire.h b/projects/test_dht11/lib/onewire.h
new file mode 100644
index 0000000..924e1d4
--- /dev/null
+++ b/projects/test_dht11/lib/onewire.h
@@ -0,0 +1,51 @@
+#pragma once
+
+//
+// Utils for Dallas OneWire bus (DS1820 etc)
+//
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define SKIP_ROM  0xCC
+#define CONVERT_T 0x44
+#define READ_SCRATCHPAD 0xBE
+
+/** Perform bus reset. Returns true if any device is connected */
+bool ow_reset(const uint8_t pin);
+
+/** Send a single byte */
+void ow_send(const uint8_t pin, const uint8_t byte);
+
+/** Read a single byte */
+uint8_t ow_read(const uint8_t pin);
+
+/** Wait until the device is ready. Returns false on timeout */
+bool ow_wait_ready(const uint8_t pin);
+
+/** Read bytes into an array */
+void ow_read_arr(const uint8_t pin, uint8_t* array, const uint8_t count);
+
+/** Compute a CRC16 checksum */
+uint16_t crc16( uint8_t *data, uint8_t len);
+
+/** Compute a CRC8 checksum */
+uint8_t crc8(uint8_t *addr, uint8_t len);
+
+// --- utils for DS1820 ---
+
+#define TEMP_ERROR -32768
+
+/**
+ * Read temperature in 0.0625°C, or TEMP_ERROR on error
+ * Use this where you'd normally use READ_SCRATCHPAD
+ */
+int16_t ds1820_read_temp(uint8_t pin);
+
+
+/**
+ * Read temperature in 0.1°C, or TEMP_ERROR on error
+ * Use this where you'd normally use READ_SCRATCHPAD
+ */
+int16_t ds1820_read_temp_c(uint8_t pin);
+
diff --git a/projects/test_dht11/lib/sonar.c b/projects/test_dht11/lib/sonar.c
new file mode 100644
index 0000000..d392d04
--- /dev/null
+++ b/projects/test_dht11/lib/sonar.c
@@ -0,0 +1,139 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "iopins.h"
+#include "sonar.h"
+
+// Currently measured sonar
+static sonar_t* _so;
+
+// Flag that measurement is in progress
+volatile bool sonar_busy;
+
+// Result of last measurement, in millimeters
+volatile int16_t sonar_result;
+
+
+void _sonar_init_do(sonar_t* so, PORT_P port, uint8_t ntx, PORT_P pin, uint8_t nrx)
+{
+	so->port = port;
+	so->ntx = ntx;
+	so->pin = pin;
+	so->nrx = nrx;
+
+	switch((const uint16_t) pin) {
+		case ((const uint16_t) &PINB): so->bank = 0; break;
+		case ((const uint16_t) &PINC): so->bank = 1; break;
+		case ((const uint16_t) &PIND): so->bank = 2; break;
+	}
+}
+
+
+/**
+ * Start sonar measurement
+ * Interrupts must be enabled
+ * TIMER 1 will be used for the async measurement
+ * Timer 1 overflow and Pin Change interrupts must invoke Sonar handlers.
+ */
+bool sonar_start(sonar_t* so)
+{
+	if (sonar_busy) return false;
+
+	_so = so;
+
+	sonar_busy = true;
+
+	// make sure the timer is stopped (set clock to NONE)
+	TCCR1B = 0;
+
+	// Timer overflow interrupt enable
+	// We'll stop measuring on overflow
+	sbi(TIMSK1, TOIE1);
+
+	// Clear the timer value
+	TCNT1 = 0;
+
+	// Set up pin change interrupt mask for the RX pin
+	switch(so->bank) {
+		case 0: sbi(PCMSK0, so->nrx); break;
+		case 1: sbi(PCMSK1, so->nrx); break;
+		case 2: sbi(PCMSK2, so->nrx); break;
+	}
+
+	// send positive pulse
+	sbi_p(so->port, so->ntx);
+	_delay_us(_SNR_TRIG_TIME);
+	cbi_p(so->port, so->ntx);
+
+	// Wait for start of response
+	while (bit_is_low_p(so->pin, so->nrx));
+
+	// Set timer clock source: F_CPU / 8 (0.5 us resolution)
+	TCCR1B = (0b010 << CS10);
+
+	// Enable pin change interrupt
+	sbi(PCICR, so->bank);
+
+	return true;
+}
+
+
+/** Stop the timer */
+void _sonar_stop()
+{
+	// stop timer
+	TCCR1B = 0;
+
+	// Disable RX pin interrupt mask
+	switch(_so->bank) {
+		case 0: PCMSK0 &= ~(1 << (_so->nrx)); break;
+		case 1: PCMSK1 &= ~(1 << (_so->nrx)); break;
+		case 2: PCMSK2 &= ~(1 << (_so->nrx)); break;
+	}
+
+	// Disable timer1 overflow interrupt
+	cbi(TIMSK1, TOIE1);
+
+	sonar_busy = false;
+}
+
+
+/** Handle TIMER1_OVF (returns true if consumed) */
+inline bool sonar_handle_t1ovf()
+{
+	if (!sonar_busy) return false; // nothing
+
+	sonar_result = -1;
+	_sonar_stop();
+
+	return true;
+}
+
+
+/** Handle pin change interrupt (returns true if consumed) */
+inline bool sonar_handle_pci()
+{
+	if (!sonar_busy) {
+		return false; // nothing
+	}
+
+	if (bit_is_high_p(_so->pin, _so->nrx)) {
+		// rx is high, not our pin change event
+		return false;
+	}
+
+	uint64_t x = TCNT1;
+	x /= _SNR_DIV_CONST;
+	x *= 100000000L;
+	x /= F_CPU;
+	sonar_result = (int16_t) x;
+
+	// no obstacle
+	if (sonar_result > _SNR_MAX_DIST) sonar_result = -1;
+
+	_sonar_stop();
+
+	return true;
+}
diff --git a/projects/test_dht11/lib/sonar.h b/projects/test_dht11/lib/sonar.h
new file mode 100644
index 0000000..0a92764
--- /dev/null
+++ b/projects/test_dht11/lib/sonar.h
@@ -0,0 +1,66 @@
+#pragma once
+
+//
+// Utilities for working with the HC-SR04 ultrasonic sensor
+// Can be easily modified to work with other similar modules
+//
+// It's required that you call the sonar_handle_* functions from your ISRs
+// See example program for more info.
+//
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "iopins.h"
+
+// Calib constant for the module
+// CM = uS / _DIV_CONST
+#define _SNR_DIV_CONST 58
+
+// Max module distance in MM
+#define _SNR_MAX_DIST 4000
+
+// Trigger time in uS
+#define _SNR_TRIG_TIME 10
+
+
+// Sonar data object
+typedef struct {
+	PORT_P port; // Tx PORT
+	uint8_t ntx; // Tx bit number
+	PORT_P pin;  // Rx PIN
+	uint8_t nrx; // Rx bit number
+	uint8_t bank; // Rx PCINT bank
+} sonar_t;
+
+
+extern volatile bool sonar_busy;
+extern volatile int16_t sonar_result;
+
+
+// Create a Sonar port
+// Args: sonar_t* so, Trig pin, Echo pin
+#define sonar_init(so, trig, echo) do { \
+	as_output(trig); \
+	as_input_pu(echo); \
+	_sonar_init_do(so, &_port(trig), _pn(trig), &_pin(echo), _pn(echo)); \
+} while(0)
+
+// private, in header because of the macro.
+void _sonar_init_do(sonar_t* so, PORT_P port, uint8_t ntx, PORT_P pin, uint8_t nrx);
+
+
+/**
+ * Start sonar measurement
+ * Interrupts must be enabled
+ * TIMER 1 will be used for the async measurement
+ */
+bool sonar_start(sonar_t* so);
+
+
+/** Handle TIMER1_OVF (returns true if consumed) */
+bool sonar_handle_t1ovf();
+
+
+/** Handle pin change interrupt (returns true if consumed) */
+bool sonar_handle_pci();
diff --git a/projects/onewire_test/lib/stream.c b/projects/test_dht11/lib/stream.c
similarity index 100%
rename from projects/onewire_test/lib/stream.c
rename to projects/test_dht11/lib/stream.c
diff --git a/projects/onewire_test/lib/stream.h b/projects/test_dht11/lib/stream.h
similarity index 100%
rename from projects/onewire_test/lib/stream.h
rename to projects/test_dht11/lib/stream.h
diff --git a/projects/onewire_test/lib/uart.c b/projects/test_dht11/lib/uart.c
similarity index 100%
rename from projects/onewire_test/lib/uart.c
rename to projects/test_dht11/lib/uart.c
diff --git a/projects/onewire_test/lib/uart.h b/projects/test_dht11/lib/uart.h
similarity index 100%
rename from projects/onewire_test/lib/uart.h
rename to projects/test_dht11/lib/uart.h
diff --git a/projects/test_dht11/lib/wsrgb.c b/projects/test_dht11/lib/wsrgb.c
new file mode 100644
index 0000000..8eb2507
--- /dev/null
+++ b/projects/test_dht11/lib/wsrgb.c
@@ -0,0 +1,132 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <stdint.h>
+
+#include "iopins.h"
+#include "nsdelay.h"
+
+#include "wsrgb.h"
+#include "color.h"
+#include "ws_config.h"
+
+
+/* Driver code for WS2812B */
+
+void ws_init()
+{
+	as_output(WS_PIN);
+}
+
+/** Wait long enough for the colors to show */
+void ws_show() {
+	delay_ns_c(WS_T_LATCH, 0);
+}
+
+/** Send one byte to the RGB strip */
+void ws_send_byte(const uint8_t bb)
+{
+	for (volatile int8_t i = 7; i >= 0; --i) {
+		if ((bb) & (1 << i)) {
+			pin_high(WS_PIN);
+			delay_ns_c(WS_T_1H, -2);
+
+			pin_low(WS_PIN);
+			delay_ns_c(WS_T_1L, -10);
+		} else {
+			pin_high(WS_PIN);
+			delay_ns_c(WS_T_0H, -2);
+
+			pin_low(WS_PIN);
+			delay_ns_c(WS_T_0L, -10);
+		}
+	}
+}
+
+
+/** Send R,G,B color to the strip */
+void ws_send_rgb(const uint8_t r, const uint8_t g, const uint8_t b)
+{
+	ws_send_byte(g);
+	ws_send_byte(r);
+	ws_send_byte(b);
+}
+
+
+/** Send a RGB struct */
+void ws_send_xrgb(xrgb_t xrgb)
+{
+	ws_send_byte(xrgb.g);
+	ws_send_byte(xrgb.r);
+	ws_send_byte(xrgb.b);
+}
+
+
+/** Send color hex */
+void ws_send_rgb24(rgb24_t rgb)
+{
+	ws_send_byte(rgb24_g(rgb));
+	ws_send_byte(rgb24_r(rgb));
+	ws_send_byte(rgb24_b(rgb));
+}
+
+/** Send array of colors */
+void ws_send_xrgb_array(const xrgb_t rgbs[], const uint8_t length)
+{
+	for (uint8_t i = 0; i < length; i++) {
+		const xrgb_t c = rgbs[i];
+		ws_send_byte(c.g);
+		ws_send_byte(c.r);
+		ws_send_byte(c.b);
+	}
+}
+
+/** Send array of colors */
+void ws_send_rgb24_array(const rgb24_t rgbs[], const uint8_t length)
+{
+	for (uint8_t i = 0; i < length; i++) {
+		const rgb24_t c = rgbs[i];
+		ws_send_byte(rgb24_g(c));
+		ws_send_byte(rgb24_r(c));
+		ws_send_byte(rgb24_b(c));
+	}
+}
+
+//#define ws_send_rgb24_array(rgbs, length) __ws_send_array_proto((rgbs), (length), rgb24)
+
+// prototype for sending array. it's ugly, sorry.
+/*#define __ws_send_array_proto(rgbs, length, style) {						\
+	for (uint8_t __rgb_sap_i = 0; __rgb_sap_i < length; __rgb_sap_i++) {			\
+		style ## _t __rgb_sap2 = (rgbs)[__rgb_sap_i];								\
+		ws_send_ ## style(__rgb_sap2);								\
+	}																			\
+}*/
+
+
+// /** Send a 2D array to a zig-zag display */
+// #define ws_send_xrgb_array_zigzag(rgbs, width, height) {				\
+// 	int8_t __rgb_sxaz_y, __rgb_sxaz_x;										\
+// 	for(__rgb_sxaz_y = 0; __rgb_sxaz_y < (height); __rgb_sxaz_y ++) {			\
+// 		for(__rgb_sxaz_x = 0; __rgb_sxaz_x < (width); __rgb_sxaz_x++) {		\
+// 			ws_send_xrgb((rgbs)[__rgb_sxaz_y][__rgb_sxaz_x]);	\
+// 		}																	\
+// 		__rgb_sxaz_y++;														\
+// 		for(__rgb_sxaz_x = (width) - 1; __rgb_sxaz_x >= 0; __rgb_sxaz_x--) {	\
+// 			ws_send_xrgb((rgbs)[__rgb_sxaz_y][__rgb_sxaz_x]);	\
+// 		}																	\
+// 	}																		\
+// }
+
+
+// /* Send a linear array to a zig-zag display as a n*m board (row-by-row)
+// #define ws_send_xrgb_array_zigzag_linear(rgbs, width, height) {					\
+// 	int8_t __rgb_sxazl_x, __rgb_sxazl_y;													\
+// 	for(__rgb_sxazl_y = 0; __rgb_sxazl_y < (height); __rgb_sxazl_y++) {					\
+// 		for(__rgb_sxazl_x = 0; __rgb_sxazl_x < (width); __rgb_sxazl_x++) {					\
+// 			ws_send_xrgb((rgbs)[__rgb_sxazl_y * (width) + __rgb_sxazl_x]);	\
+// 		}																				\
+// 		__rgb_sxazl_y++;																	\
+// 		for(__rgb_sxazl_x = width-1; __rgb_sxazl_x >=0; __rgb_sxazl_x--) {					\
+// 			ws_send_xrgb((rgbs)[__rgb_sxazl_y * (width) + __rgb_sxazl_x]);	\
+// 		}																				\
+// 	}																					\
+// }
diff --git a/projects/test_dht11/lib/wsrgb.h b/projects/test_dht11/lib/wsrgb.h
new file mode 100644
index 0000000..391bd1f
--- /dev/null
+++ b/projects/test_dht11/lib/wsrgb.h
@@ -0,0 +1,51 @@
+#pragma once
+
+//
+// Utils for driving a WS2812 RGB LED strips, and color manipulation in general.
+//
+// Timing is critical!
+//
+// Create a config file rgb_config.h next to your main.c
+//
+
+#include "iopins.h"
+#include "color.h"
+
+// Your config file
+#include "ws_config.h"
+
+/*
+	#define WS_T_1H  700
+	#define WS_T_1L  150
+	#define WS_T_0H  150
+	#define WS_T_0L  700
+	#define WS_T_LATCH 7000
+	#define WS_PIN 2
+*/
+
+
+// --- functions for RGB strips ---
+
+/** Initialize OI */
+void ws_init();
+
+/** Wait long enough for the colors to show */
+void ws_show();
+
+/** Send one byte to the RGB strip */
+void ws_send_byte(const uint8_t bb);
+
+/** Send R,G,B color to the strip */
+void ws_send_rgb(const uint8_t r, const uint8_t g, const uint8_t b);
+
+/** Send a RGB struct */
+void ws_send_xrgb(xrgb_t xrgb);
+
+/** Send color hex */
+void ws_send_rgb24(rgb24_t rgb);
+
+/** Send array of colors */
+void ws_send_xrgb_array(const xrgb_t rgbs[], const uint8_t length);
+
+/** Send array of colors */
+void ws_send_rgb24_array(const rgb24_t rgbs[], const uint8_t length);
diff --git a/projects/test_dht11/main.c b/projects/test_dht11/main.c
new file mode 100644
index 0000000..c85a07e
--- /dev/null
+++ b/projects/test_dht11/main.c
@@ -0,0 +1,36 @@
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "lib/iopins.h"
+#include "lib/uart.h"
+#include "lib/stream.h"
+#include "lib/dht11.h"
+
+void main()
+{
+	uart_init(9600);
+	uart_puts_P(PSTR("*** DHT11 test ***\r\n"));
+
+	while(1)
+	{
+		dht11_result_t result;
+
+		if (dht11_read(2, &result))
+		{
+			put_i8(uart, result.temp);
+			uart_puts_P(PSTR(" °C, "));
+			put_i8(uart, result.rh);
+			uart_puts_P(PSTR(" %r.H\r\n"));
+		}
+		else
+		{
+			uart_puts_P(PSTR("Read error!\r\n"));
+		}
+
+		_delay_ms(500);
+	}
+}
diff --git a/projects/onewire_test/Makefile b/projects/test_onewire/Makefile
similarity index 100%
rename from projects/onewire_test/Makefile
rename to projects/test_onewire/Makefile
diff --git a/projects/test_onewire/lib/calc.h b/projects/test_onewire/lib/calc.h
new file mode 100644
index 0000000..58f27bf
--- /dev/null
+++ b/projects/test_onewire/lib/calc.h
@@ -0,0 +1,87 @@
+#pragma once
+
+//
+// Bit and byte manipulation utilities
+//
+
+
+// --- Increment in range ---
+// when overflown, wraps within range. Lower bound < upper bound.
+// ..., upper bound excluded
+#define inc_wrap(var, min, max)  do { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } while(0)
+// ..., upper bound included
+#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1)
+
+
+// --- Decrement in range ---
+// when underflown, wraps within range. Lower bound < upper bound.
+// ..., upper bound excluded
+#define dec_wrap(var, min, max)  do { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } while(0)
+// ..., upper bound included
+#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
+
+
+// --- Bit manipulation --
+
+// Set bit
+#define sbi(reg, bit) do { (reg) |=  (1 << (uint8_t)(bit)); } while(0)
+
+// Clear bit
+#define cbi(reg, bit) do { (reg) &= ~(1 << (uint8_t)(bit)); } while(0)
+
+// Get n-th bit
+#define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1)
+
+// Test n-th bit (Can't use bit_is_set, as it's redefined in sfr_def.h)
+#define bit_is_high(reg, bit) get_bit(reg, bit)
+#define bit_is_low(reg, bit)  (!get_bit(reg, bit))
+
+// Write value to n-th bit
+#define set_bit(reg, bit, value) do { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0)
+
+// Invert n-th bit
+#define toggle_bit(reg, bit)       do { (reg) ^= (1 << (uint8_t)(bit)); } while(0)
+
+
+// --- Bit manipulation with pointer to variable ---
+
+// Set n-th bit in pointee
+#define sbi_p(reg_p, bit) do { (*(reg_p)) |=  (1 << (uint8_t)(bit)); } while(0)
+// Clear n-th bit in pointee
+#define cbi_p(reg_p, bit) do { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } while(0)
+
+// Get n-th bit in pointee
+#define get_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1)
+
+// Test n-th bit in pointee (Can't use bit_is_set, as it's redefined in sfr_def.h)
+#define bit_is_high_p(reg_p, bit) get_bit_p(reg_p, bit)
+#define bit_is_low_p(reg_p, bit)  (!get_bit_p(reg_p, bit))
+
+// Write value to a bit in pointee
+#define set_bit_p(reg_p, bit, value) do { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bit) & 0x1))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0)
+#define toggle_bit_p(reg_p, bit)       do { *(reg_p) ^= (1 << (uint8_t)(bit)); } while(0)
+
+
+// --- Nibble manipulation ---
+
+// Replace nibble in a byte
+#define set_low_nibble(reg, value)  do { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0)
+#define set_high_nibble(reg, value) do { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
+
+#define set_low_nibble_p(reg_p, value)  do { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0)
+#define set_high_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
+
+#define low_nibble(x) ((uint8_t)(x) & 0xF)
+#define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4)
+
+// --- Range tests ---
+
+// Test if X is within low..high, regardless of bounds order
+#define in_range(x, low, high)  ((((low)  < (high)) && ((x) >= (low) && (x)  < (high))) || (((low) > (high)) && ((x) >= (high) && (x)  < (low))))
+// ..., include greater bound
+#define in_rangei(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (high) && (x) <= (low))))
+
+// Test if X in low..high, wrap around ends if needed.
+#define in_range_wrap(x, low, high)  ((((low)  < (high)) && ((x) >= (low) && (x) < (high)))  || (((low) > (high)) && ((x) >= (low) || (x)  < (high))))
+// ..., include upper bound
+#define in_range_wrapi(x, low, high) ((((low) <= (high)) && ((x) >= (low) && (x) <= (high))) || (((low) > (high)) && ((x) >= (low) || (x) <= (high))))
diff --git a/projects/test_onewire/lib/iopins.c b/projects/test_onewire/lib/iopins.c
new file mode 100644
index 0000000..2b3934b
--- /dev/null
+++ b/projects/test_onewire/lib/iopins.c
@@ -0,0 +1,276 @@
+#include <avr/io.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "calc.h"
+#include "iopins.h"
+
+
+void set_dir_n(const uint8_t pin, const uint8_t d)
+{
+	switch(pin) {
+		case 0: set_dir(0, d); return;
+		case 1: set_dir(1, d); return;
+		case 2: set_dir(2, d); return;
+		case 3: set_dir(3, d); return;
+		case 4: set_dir(4, d); return;
+		case 5: set_dir(5, d); return;
+		case 6: set_dir(6, d); return;
+		case 7: set_dir(7, d); return;
+		case 8: set_dir(8, d); return;
+		case 9: set_dir(9, d); return;
+		case 10: set_dir(10, d); return;
+		case 11: set_dir(11, d); return;
+		case 12: set_dir(12, d); return;
+		case 13: set_dir(13, d); return;
+		case 14: set_dir(14, d); return;
+		case 15: set_dir(15, d); return;
+		case 16: set_dir(16, d); return;
+		case 17: set_dir(17, d); return;
+		case 18: set_dir(18, d); return;
+		case 19: set_dir(19, d); return;
+		case 20: set_dir(20, d); return;
+		case 21: set_dir(21, d); return;
+	}
+}
+
+void as_input_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: as_input(0); return;
+		case 1: as_input(1); return;
+		case 2: as_input(2); return;
+		case 3: as_input(3); return;
+		case 4: as_input(4); return;
+		case 5: as_input(5); return;
+		case 6: as_input(6); return;
+		case 7: as_input(7); return;
+		case 8: as_input(8); return;
+		case 9: as_input(9); return;
+		case 10: as_input(10); return;
+		case 11: as_input(11); return;
+		case 12: as_input(12); return;
+		case 13: as_input(13); return;
+		case 14: as_input(14); return;
+		case 15: as_input(15); return;
+		case 16: as_input(16); return;
+		case 17: as_input(17); return;
+		case 18: as_input(18); return;
+		case 19: as_input(19); return;
+		case 20: as_input(20); return;
+		case 21: as_input(21); return;
+	}
+}
+
+
+void as_input_pu_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: as_input_pu(0); return;
+		case 1: as_input_pu(1); return;
+		case 2: as_input_pu(2); return;
+		case 3: as_input_pu(3); return;
+		case 4: as_input_pu(4); return;
+		case 5: as_input_pu(5); return;
+		case 6: as_input_pu(6); return;
+		case 7: as_input_pu(7); return;
+		case 8: as_input_pu(8); return;
+		case 9: as_input_pu(9); return;
+		case 10: as_input_pu(10); return;
+		case 11: as_input_pu(11); return;
+		case 12: as_input_pu(12); return;
+		case 13: as_input_pu(13); return;
+		case 14: as_input_pu(14); return;
+		case 15: as_input_pu(15); return;
+		case 16: as_input_pu(16); return;
+		case 17: as_input_pu(17); return;
+		case 18: as_input_pu(18); return;
+		case 19: as_input_pu(19); return;
+		case 20: as_input_pu(20); return;
+		case 21: as_input_pu(21); return;
+	}
+}
+
+
+void as_output_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: as_output(0); return;
+		case 1: as_output(1); return;
+		case 2: as_output(2); return;
+		case 3: as_output(3); return;
+		case 4: as_output(4); return;
+		case 5: as_output(5); return;
+		case 6: as_output(6); return;
+		case 7: as_output(7); return;
+		case 8: as_output(8); return;
+		case 9: as_output(9); return;
+		case 10: as_output(10); return;
+		case 11: as_output(11); return;
+		case 12: as_output(12); return;
+		case 13: as_output(13); return;
+		case 14: as_output(14); return;
+		case 15: as_output(15); return;
+		case 16: as_output(16); return;
+		case 17: as_output(17); return;
+		case 18: as_output(18); return;
+		case 19: as_output(19); return;
+		case 20: as_output(20); return;
+		case 21: as_output(21); return;
+	}
+}
+
+void set_pin_n(const uint8_t pin, const uint8_t v)
+{
+	switch(pin) {
+		case 0: set_pin(0, v); return;
+		case 1: set_pin(1, v); return;
+		case 2: set_pin(2, v); return;
+		case 3: set_pin(3, v); return;
+		case 4: set_pin(4, v); return;
+		case 5: set_pin(5, v); return;
+		case 6: set_pin(6, v); return;
+		case 7: set_pin(7, v); return;
+		case 8: set_pin(8, v); return;
+		case 9: set_pin(9, v); return;
+		case 10: set_pin(10, v); return;
+		case 11: set_pin(11, v); return;
+		case 12: set_pin(12, v); return;
+		case 13: set_pin(13, v); return;
+		case 14: set_pin(14, v); return;
+		case 15: set_pin(15, v); return;
+		case 16: set_pin(16, v); return;
+		case 17: set_pin(17, v); return;
+		case 18: set_pin(18, v); return;
+		case 19: set_pin(19, v); return;
+		case 20: set_pin(20, v); return;
+		case 21: set_pin(21, v); return;
+	}
+}
+
+void pin_low_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: pin_low(0); return;
+		case 1: pin_low(1); return;
+		case 2: pin_low(2); return;
+		case 3: pin_low(3); return;
+		case 4: pin_low(4); return;
+		case 5: pin_low(5); return;
+		case 6: pin_low(6); return;
+		case 7: pin_low(7); return;
+		case 8: pin_low(8); return;
+		case 9: pin_low(9); return;
+		case 10: pin_low(10); return;
+		case 11: pin_low(11); return;
+		case 12: pin_low(12); return;
+		case 13: pin_low(13); return;
+		case 14: pin_low(14); return;
+		case 15: pin_low(15); return;
+		case 16: pin_low(16); return;
+		case 17: pin_low(17); return;
+		case 18: pin_low(18); return;
+		case 19: pin_low(19); return;
+		case 20: pin_low(20); return;
+		case 21: pin_low(21); return;
+	}
+}
+
+void pin_high_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: pin_high(0); return;
+		case 1: pin_high(1); return;
+		case 2: pin_high(2); return;
+		case 3: pin_high(3); return;
+		case 4: pin_high(4); return;
+		case 5: pin_high(5); return;
+		case 6: pin_high(6); return;
+		case 7: pin_high(7); return;
+		case 8: pin_high(8); return;
+		case 9: pin_high(9); return;
+		case 10: pin_high(10); return;
+		case 11: pin_high(11); return;
+		case 12: pin_high(12); return;
+		case 13: pin_high(13); return;
+		case 14: pin_high(14); return;
+		case 15: pin_high(15); return;
+		case 16: pin_high(16); return;
+		case 17: pin_high(17); return;
+		case 18: pin_high(18); return;
+		case 19: pin_high(19); return;
+		case 20: pin_high(20); return;
+		case 21: pin_high(21); return;
+	}
+}
+
+
+void toggle_pin_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: toggle_pin(0); return;
+		case 1: toggle_pin(1); return;
+		case 2: toggle_pin(2); return;
+		case 3: toggle_pin(3); return;
+		case 4: toggle_pin(4); return;
+		case 5: toggle_pin(5); return;
+		case 6: toggle_pin(6); return;
+		case 7: toggle_pin(7); return;
+		case 8: toggle_pin(8); return;
+		case 9: toggle_pin(9); return;
+		case 10: toggle_pin(10); return;
+		case 11: toggle_pin(11); return;
+		case 12: toggle_pin(12); return;
+		case 13: toggle_pin(13); return;
+		case 14: toggle_pin(14); return;
+		case 15: toggle_pin(15); return;
+		case 16: toggle_pin(16); return;
+		case 17: toggle_pin(17); return;
+		case 18: toggle_pin(18); return;
+		case 19: toggle_pin(19); return;
+		case 20: toggle_pin(20); return;
+		case 21: toggle_pin(21); return;
+	}
+}
+
+
+bool get_pin_n(const uint8_t pin)
+{
+	switch(pin) {
+		case 0: return get_pin(0);
+		case 1: return get_pin(1);
+		case 2: return get_pin(2);
+		case 3: return get_pin(3);
+		case 4: return get_pin(4);
+		case 5: return get_pin(5);
+		case 6: return get_pin(6);
+		case 7: return get_pin(7);
+		case 8: return get_pin(8);
+		case 9: return get_pin(9);
+		case 10: return get_pin(10);
+		case 11: return get_pin(11);
+		case 12: return get_pin(12);
+		case 13: return get_pin(13);
+		case 14: return get_pin(14);
+		case 15: return get_pin(15);
+		case 16: return get_pin(16);
+		case 17: return get_pin(17);
+		case 18: return get_pin(18);
+		case 19: return get_pin(19);
+		case 20: return get_pin(20);
+		case 21: return get_pin(21);
+	}
+	return false;
+}
+
+
+bool is_low_n(const uint8_t pin)
+{
+	return !get_pin_n(pin);
+}
+
+
+bool is_high_n(const uint8_t pin)
+{
+	return get_pin_n(pin);
+}
diff --git a/projects/test_onewire/lib/iopins.h b/projects/test_onewire/lib/iopins.h
new file mode 100644
index 0000000..3cabadd
--- /dev/null
+++ b/projects/test_onewire/lib/iopins.h
@@ -0,0 +1,213 @@
+#pragma once
+
+//
+// * Utilities for pin aliasing / numbering. *
+//
+// Designed for Arduino.
+//
+// If you know the pin number beforehand, you can use the macros.
+//
+// If you need to use a variable for pin number, use the `_n` functions.
+// They are much slower, so always check if you really need them
+// - and they aren't fit for things where precise timing is required.
+//
+
+#include <avr/io.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "calc.h"
+
+
+// type: pointer to port
+typedef volatile uint8_t* PORT_P;
+
+
+/** Pin numbering reference */
+#define D0 0
+#define D1 1
+#define D2 2
+#define D3 3
+#define D4 4
+#define D5 5
+#define D6 6
+#define D7 7
+#define D8 8
+#define D9 9
+#define D10 10
+#define D11 11
+#define D12 12
+#define D13 13
+#define D14 14
+#define D15 15
+#define D16 16
+#define D17 17
+#define D18 18
+#define D19 19
+#define D20 20
+#define D21 21
+#define A0 14
+#define A1 15
+#define A2 16
+#define A3 17
+#define A4 18
+#define A5 19
+#define A6 20
+#define A7 21
+
+
+#define _ddr(pin)  _DDR_##pin
+#define _pin(pin)  _PIN_##pin
+#define _pn(pin)   _PN_##pin
+#define _port(pin) _PORT_##pin
+
+
+/** Set pin direction */
+#define set_dir(pin, d)  set_bit( _ddr(pin), _pn(pin), d )
+void    set_dir_n(const uint8_t pin, const uint8_t d);
+
+
+/** Configure pin as input */
+#define as_input(pin)    cbi( _ddr(pin), _pn(pin) )
+void    as_input_n(const uint8_t pin);
+
+
+/** Configure pin as input, with pull-up enabled */
+#define as_input_pu(pin) { as_input(pin); pin_high(pin); }
+void    as_input_pu_n(const uint8_t pin);
+
+
+/** Configure pin as output */
+#define as_output(pin)   sbi( _ddr(pin), _pn(pin) )
+void    as_output_n(const uint8_t pin);
+
+
+/** Write value to a pin */
+#define set_pin(pin, v) set_bit( _port(pin), _pn(pin), v )
+void    set_pin_n(const uint8_t pin, const uint8_t v);
+
+
+/** Write 0 to a pin */
+#define pin_low(pin)    cbi( _port(pin), _pn(pin) )
+void    pin_low_n(const uint8_t pin);
+
+
+/** Write 1 to a pin */
+#define pin_high(pin)   sbi( _port(pin), _pn(pin) )
+void    pin_high_n(const uint8_t pin);
+
+
+/** Toggle a pin state */
+#define toggle_pin(pin)   sbi( _pin(pin), _pn(pin) )
+void    toggle_pin_n(const uint8_t pin);
+
+
+/** Read a pin value */
+#define get_pin(pin)  get_bit( _pin(pin), _pn(pin) )
+bool    get_pin_n(const uint8_t pin);
+
+
+/** CHeck if pin is low */
+#define is_low(pin)   (get_pin(pin) == 0)
+bool    is_low_n(const uint8_t pin);
+
+
+/** CHeck if pin is high */
+#define is_high(pin)  (get_pin(pin) != 0)
+bool    is_high_n(const uint8_t pin);
+
+
+
+// Helper macros
+
+#define _PORT_0  PORTD
+#define _PORT_1  PORTD
+#define _PORT_2  PORTD
+#define _PORT_3  PORTD
+#define _PORT_4  PORTD
+#define _PORT_5  PORTD
+#define _PORT_6  PORTD
+#define _PORT_7  PORTD
+#define _PORT_8  PORTB
+#define _PORT_9  PORTB
+#define _PORT_10 PORTB
+#define _PORT_11 PORTB
+#define _PORT_12 PORTB
+#define _PORT_13 PORTB
+#define _PORT_14 PORTC
+#define _PORT_15 PORTC
+#define _PORT_16 PORTC
+#define _PORT_17 PORTC
+#define _PORT_18 PORTC
+#define _PORT_19 PORTC
+#define _PORT_20 PORTC
+#define _PORT_21 PORTC
+
+#define _PIN_0  PIND
+#define _PIN_1  PIND
+#define _PIN_2  PIND
+#define _PIN_3  PIND
+#define _PIN_4  PIND
+#define _PIN_5  PIND
+#define _PIN_6  PIND
+#define _PIN_7  PIND
+#define _PIN_8  PINB
+#define _PIN_9  PINB
+#define _PIN_10 PINB
+#define _PIN_11 PINB
+#define _PIN_12 PINB
+#define _PIN_13 PINB
+#define _PIN_14 PINC
+#define _PIN_15 PINC
+#define _PIN_16 PINC
+#define _PIN_17 PINC
+#define _PIN_18 PINC
+#define _PIN_19 PINC
+#define _PIN_20 PINC
+#define _PIN_21 PINC
+
+#define _DDR_0  DDRD
+#define _DDR_1  DDRD
+#define _DDR_2  DDRD
+#define _DDR_3  DDRD
+#define _DDR_4  DDRD
+#define _DDR_5  DDRD
+#define _DDR_6  DDRD
+#define _DDR_7  DDRD
+#define _DDR_8  DDRB
+#define _DDR_9  DDRB
+#define _DDR_10 DDRB
+#define _DDR_11 DDRB
+#define _DDR_12 DDRB
+#define _DDR_13 DDRB
+#define _DDR_14 DDRC
+#define _DDR_15 DDRC
+#define _DDR_16 DDRC
+#define _DDR_17 DDRC
+#define _DDR_18 DDRC
+#define _DDR_19 DDRC
+#define _DDR_20 DDRC
+#define _DDR_21 DDRC
+
+#define _PN_0  0
+#define _PN_1  1
+#define _PN_2  2
+#define _PN_3  3
+#define _PN_4  4
+#define _PN_5  5
+#define _PN_6  6
+#define _PN_7  7
+#define _PN_8  0
+#define _PN_9  1
+#define _PN_10 2
+#define _PN_11 3
+#define _PN_12 4
+#define _PN_13 5
+#define _PN_14 0
+#define _PN_15 1
+#define _PN_16 2
+#define _PN_17 3
+#define _PN_18 4
+#define _PN_19 5
+#define _PN_20 6
+#define _PN_21 7
diff --git a/projects/onewire_test/lib/onewire.h b/projects/test_onewire/lib/onewire.h
similarity index 100%
rename from projects/onewire_test/lib/onewire.h
rename to projects/test_onewire/lib/onewire.h
diff --git a/projects/test_onewire/lib/stream.c b/projects/test_onewire/lib/stream.c
new file mode 100644
index 0000000..9397b73
--- /dev/null
+++ b/projects/test_onewire/lib/stream.c
@@ -0,0 +1,213 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "stream.h"
+#include "calc.h"
+
+
+static char tmpstr[20]; // buffer for number rendering
+
+
+void put_str(const STREAM *p, char* str)
+{
+	char c;
+	while ((c = *str++)) {
+		p->tx(c);
+	}
+}
+
+
+void put_str_P(const STREAM *p, const char* str)
+{
+	char c;
+	while ((c = pgm_read_byte(str++))) {
+		p->tx(c);
+	}
+}
+
+
+static void _putnf(const STREAM *p, const uint8_t places);
+
+
+/** Send signed int8 */
+void put_u8(const STREAM *p, const uint8_t num)
+{
+	utoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+/** Send unsigned int8 */
+void put_i8(const STREAM *p, const int8_t num)
+{
+	itoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+
+/** Send unsigned int */
+void put_u16(const STREAM *p, const uint16_t num)
+{
+	utoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+/** Send signed int */
+void put_i16(const STREAM *p, const int16_t num)
+{
+	itoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+/** Send unsigned long */
+void put_u32(const STREAM *p, const uint32_t num)
+{
+	ultoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+/** Send signed long */
+void put_i32(const STREAM *p, const int32_t num)
+{
+	ltoa(num, tmpstr, 10);
+	put_str(p, tmpstr);
+}
+
+
+/** Print number as hex */
+void _print_hex(const STREAM *p, uint8_t* start, uint8_t bytes)
+{
+	for (; bytes > 0; bytes--) {
+		uint8_t b = *(start + bytes - 1);
+
+		for(uint8_t j = 0; j < 2; j++) {
+			uint8_t x = high_nibble(b);
+			b = b << 4;
+			if (x < 0xA) {
+				p->tx('0' + x);
+			} else {
+				p->tx('A' + (x - 0xA));
+			}
+		}
+	}
+}
+
+
+/** Send unsigned int8 */
+void put_x8(const STREAM *p, const uint8_t num)
+{
+	_print_hex(p, (uint8_t*) &num, 1);
+}
+
+
+/** Send int as hex */
+void put_x16(const STREAM *p, const uint16_t num)
+{
+	_print_hex(p, (uint8_t*) &num, 2);
+}
+
+
+/** Send long as hex */
+void put_x32(const STREAM *p, const uint32_t num)
+{
+	_print_hex(p, (uint8_t*) &num, 4);
+}
+
+
+/** Send long long as hex */
+void put_x64(const STREAM *p, const uint64_t num)
+{
+	_print_hex(p, (uint8_t*) &num, 8);
+}
+
+
+
+// float variant doesn't make sense for 8-bit int
+
+/** Send unsigned int as float */
+void put_u16f(const STREAM *p, const uint16_t num, const uint8_t places)
+{
+	utoa(num, tmpstr, 10);
+	_putnf(p, places);
+}
+
+
+/** Send signed int as float */
+void put_i16f(const STREAM *p, const int16_t num, const uint8_t places)
+{
+	if (num < 0) {
+		p->tx('-');
+		itoa(-num, tmpstr, 10);
+	} else {
+		itoa(num, tmpstr, 10);
+	}
+
+	_putnf(p, places);
+}
+
+
+/** Send unsigned long as float */
+void put_u32f(const STREAM *p, const uint32_t num, const uint8_t places)
+{
+	ultoa(num, tmpstr, 10);
+	_putnf(p, places);
+}
+
+
+/** Send signed long as float */
+void put_i32f(const STREAM *p, const int32_t num, const uint8_t places)
+{
+	if (num < 0) {
+		p->tx('-');
+		ltoa(-num, tmpstr, 10);
+	} else {
+		ltoa(num, tmpstr, 10);
+	}
+
+	_putnf(p, places);
+}
+
+
+/** Print number in tmp string as float with given decimal point position */
+void _putnf(const STREAM *p, const uint8_t places)
+{
+	// measure text length
+	uint8_t len = 0;
+	while(tmpstr[len] != 0) len++;
+
+	int8_t at = len - places;
+
+	// print virtual zeros
+	if (at <= 0) {
+		p->tx('0');
+		p->tx('.');
+		while(at <= -1) {
+			p->tx('0');
+			at++;
+		}
+		at = -1;
+	}
+
+	// print the number
+	uint8_t i = 0;
+	while(i < len) {
+		if (at-- == 0) {
+			p->tx('.');
+		}
+
+		p->tx(tmpstr[i++]);
+	}
+}
+
+
+/** Print CR LF */
+void put_nl(const STREAM *p)
+{
+	p->tx(13);
+	p->tx(10);
+}
diff --git a/projects/test_onewire/lib/stream.h b/projects/test_onewire/lib/stream.h
new file mode 100644
index 0000000..8ed7979
--- /dev/null
+++ b/projects/test_onewire/lib/stream.h
@@ -0,0 +1,100 @@
+#pragma once
+
+//
+// Streams -- in this library -- are instances of type STREAM.
+//
+// A stream can be used for receiving and sending bytes, generally
+// it's a pipe to a device.
+//
+// They are designed for printing numbers and strings, but can
+// also be used for general data transfer.
+//
+// Examples of streams:
+// "uart.h" -> declares global variable "uart" which is a pointer to the UART stream
+// "lcd.h" -> declares a global variable "lcd" (pointer to LCD scho stream)
+//
+// Streams help avoid code duplication, since the same functions can be used
+// to format and print data to different device types.
+//
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+/** Stream structure */
+typedef struct {
+	void (*tx) (uint8_t b);
+	uint8_t (*rx) (void);
+} STREAM;
+
+
+/** Print string into a stream */
+void put_str(const STREAM *p, char* str);
+
+
+/** Print a programspace string into a stream */
+void put_str_P(const STREAM *p, const char* str);
+
+
+/** Send signed int8 */
+#define put_char(p, c) (p)->tx((c))
+void put_u8(const STREAM *p, const uint8_t num);
+
+
+/** Send unsigned int8 */
+void put_i8(const STREAM *p, const int8_t num);
+
+
+/** Send unsigned int */
+void put_u16(const STREAM *p, const uint16_t num);
+
+
+/** Send signed int */
+void put_i16(const STREAM *p, const int16_t num);
+
+
+/** Send unsigned long */
+void put_u32(const STREAM *p, const uint32_t num);
+
+
+/** Send signed long */
+void put_i32(const STREAM *p, const int32_t num);
+
+
+/** Send unsigned int8 */
+void put_x8(const STREAM *p, const uint8_t num);
+
+
+/** Send int as hex */
+void put_x16(const STREAM *p, const uint16_t num);
+
+
+/** Send long as hex */
+void put_x32(const STREAM *p, const uint32_t num);
+
+
+/** Send long long as hex */
+void put_x64(const STREAM *p, const uint64_t num);
+
+
+// float variant doesn't make sense for 8-bit int
+
+/** Send unsigned int as float */
+void put_u16f(const STREAM *p, const uint16_t num, const uint8_t places);
+
+
+/** Send signed int as float */
+void put_i16f(const STREAM *p, const int16_t num, const uint8_t places);
+
+
+/** Send unsigned long as float */
+void put_u32f(const STREAM *p, const uint32_t num, const uint8_t places);
+
+
+/** Send signed long as float */
+void put_i32f(const STREAM *p, const int32_t num, const uint8_t places);
+
+
+/** Print CR LF */
+void put_nl(const STREAM *p);
diff --git a/projects/test_onewire/lib/uart.c b/projects/test_onewire/lib/uart.c
new file mode 100644
index 0000000..e2a477b
--- /dev/null
+++ b/projects/test_onewire/lib/uart.c
@@ -0,0 +1,678 @@
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "calc.h"
+#include "uart.h"
+#include "stream.h"
+
+// Shared stream instance
+static STREAM _uart_singleton;
+STREAM* uart;
+
+
+void _uart_init_do(uint16_t ubrr) {
+	/*Set baud rate */
+	UBRR0H = (uint8_t) (ubrr >> 8);
+	UBRR0L = (uint8_t) ubrr;
+
+	// Enable Rx and Tx
+	UCSR0B = (1 << RXEN0) | (1 << TXEN0);
+
+	// 8-bit data, 1 stop bit
+	UCSR0C = (0b11 << UCSZ00);
+
+	_uart_singleton.tx = &uart_tx;
+	_uart_singleton.rx = &uart_rx;
+
+	uart = &_uart_singleton;
+}
+
+
+/** Enable or disable RX ISR */
+void uart_isr_rx(bool yes)
+{
+	set_bit(UCSR0B, RXCIE0, yes);
+}
+
+
+/** Enable or disable TX ISR (1 byte is sent) */
+void uart_isr_tx(bool yes)
+{
+	set_bit(UCSR0B, TXCIE0, yes);
+}
+
+
+/** Enable or disable DRE ISR (all is sent) */
+void uart_isr_dre(bool yes)
+{
+	set_bit(UCSR0B, UDRIE0, yes);
+}
+
+
+/** Send byte over UART */
+void uart_tx(uint8_t data)
+{
+	// Wait for transmit buffer
+	while (!uart_tx_ready());
+	// send it
+	UDR0 = data;
+}
+
+
+/** Receive one byte over UART */
+uint8_t uart_rx()
+{
+	// Wait for data to be received
+	while (!uart_rx_ready());
+	// Get and return received data from buffer
+	return UDR0;
+}
+
+
+/** Send string over UART */
+void uart_puts(const char* str)
+{
+	while (*str) {
+		uart_tx(*str++);
+	}
+}
+
+
+/** Send progmem string over UART */
+void uart_puts_P(const char* str)
+{
+	char c;
+	while ((c = pgm_read_byte(str++))) {
+		uart_tx(c);
+	}
+}
+
+
+/** Clear receive buffer */
+void uart_flush()
+{
+	uint8_t dummy;
+	while (bit_is_high(UCSR0A, RXC0)) {
+		dummy = UDR0;
+	}
+}
+
+
+// ------------- VT100 extension --------------
+
+
+void _vt_apply_style();
+void _vt_reset_attribs_do();
+void _vt_style_do();
+void _vt_color_do();
+
+
+void vt_goto(uint8_t x, uint8_t y)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, y+1); // one-based !
+	uart_tx(';');
+	put_u8(uart, x+1);
+	uart_tx('H');
+}
+
+
+void vt_goto_x(uint8_t x)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, x+1);
+	uart_tx('`');
+}
+
+
+void vt_goto_y(uint8_t y)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, y+1);
+	uart_tx('d');
+}
+
+
+void vt_move(int8_t x, int8_t y)
+{
+	vt_move_x(x);
+	vt_move_y(y);
+}
+
+
+void vt_move_x(int8_t x)
+{
+	if (x < 0) {
+		vt_left(-x);
+	} else {
+		vt_right(x);
+	}
+}
+
+
+void vt_move_y(int8_t y)
+{
+	if (y < 0) {
+		vt_up(-y);
+	} else {
+		vt_down(y);
+	}
+}
+
+
+void vt_up(uint8_t y)
+{
+	if (y == 0) return;
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, y);
+	uart_tx('A');
+}
+
+
+void vt_down(uint8_t y)
+{
+	if (y == 0) return;
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, y);
+	uart_tx('B');
+}
+
+
+void vt_left(uint8_t x)
+{
+	if (x == 0) return;
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, x);
+	uart_tx('D');
+}
+
+
+void vt_right(uint8_t x)
+{
+	if (x == 0) return;
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, x);
+	uart_tx('C');
+}
+
+
+void vt_scroll(int8_t y)
+{
+	while (y < 0) {
+		uart_tx(27);
+		uart_tx('D'); // up
+		y++;
+	}
+
+	while (y > 0) {
+		uart_tx(27);
+		uart_tx('M'); // down
+		y--;
+	}
+}
+
+
+void vt_scroll_set(uint8_t from, uint8_t to)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, from);
+	uart_tx(';');
+	put_u8(uart, to);
+	uart_tx('r');
+}
+
+
+void vt_scroll_reset()
+{
+	uart_tx(27);
+	uart_tx('[');
+	uart_tx('r');
+}
+
+
+
+typedef struct {
+	uint8_t flags;
+	uint8_t fg;
+	uint8_t bg;
+} vt_style_t;
+
+vt_style_t saved_style;
+vt_style_t current_style;
+
+void vt_save()
+{
+	uart_puts_P(PSTR("\x1B[s"));
+
+	saved_style = current_style;
+}
+
+
+void vt_restore()
+{
+	uart_puts_P(PSTR("\x1B[u"));
+
+	current_style = saved_style;
+}
+
+
+/** Disable all text attributes (excluding color) */
+void vt_attr_reset()
+{
+	current_style.flags = 0;
+
+	_vt_reset_attribs_do();
+	_vt_apply_style();
+}
+
+
+/** Set color to white on black */
+void vt_color_reset()
+{
+	current_style.fg = VT_WHITE;
+	current_style.bg = VT_BLACK;
+
+	_vt_color_do();
+}
+
+
+/** Enable or disable a text attribute */
+void vt_attr(uint8_t attribute, bool on)
+{
+	// flags are powers of two
+	// so this can handle multiple OR'd flags
+	for(uint8_t c = 1; c <= VT_FAINT; c *= 2) {
+		if (attribute & c) {
+			if (on) {
+				current_style.flags |= c;
+			} else {
+				current_style.flags &= ~c;
+			}
+		}
+	}
+
+	_vt_apply_style();
+}
+
+
+/** Send style and color commands */
+void _vt_apply_style()
+{
+	_vt_reset_attribs_do();
+	_vt_style_do();
+	_vt_color_do();
+}
+
+
+/** Set color 0..7 */
+void vt_color(uint8_t fg, uint8_t bg)
+{
+	current_style.fg = fg;
+	current_style.bg = bg;
+	_vt_color_do();
+}
+
+
+/** Set FG color 0..7 */
+void vt_color_fg(uint8_t fg)
+{
+	current_style.fg = fg;
+	_vt_color_do();
+}
+
+
+/** Set BG color 0..7 */
+void vt_color_bg(uint8_t bg)
+{
+	current_style.bg = bg;
+	_vt_color_do();
+}
+
+
+/** Send reset command */
+inline void _vt_reset_attribs_do()
+{
+	uart_puts_P(PSTR("\x1B[m")); // reset
+}
+
+
+/** Send commands for text attribs */
+void _vt_style_do()
+{
+	if (current_style.flags & VT_BOLD) {
+		uart_puts_P(PSTR("\x1B[1m"));
+	}
+
+	if (current_style.flags & VT_FAINT) {
+		uart_puts_P(PSTR("\x1B[2m"));
+	}
+
+	if (current_style.flags & VT_ITALIC) {
+		uart_puts_P(PSTR("\x1B[3m"));
+	}
+
+	if (current_style.flags & VT_UNDERLINE) {
+		uart_puts_P(PSTR("\x1B[4m"));
+	}
+
+	if (current_style.flags & VT_BLINK) {
+		uart_puts_P(PSTR("\x1B[5m"));
+	}
+
+	if (current_style.flags & VT_REVERSE) {
+		uart_puts_P(PSTR("\x1B[7m"));
+	}
+}
+
+
+/** Send commands for xolor */
+void _vt_color_do()
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, 30 + current_style.fg);
+	uart_tx(';');
+	put_u8(uart, 40 + current_style.bg);
+	uart_tx('m');
+}
+
+
+/** Insert blank lines febore the current line */
+void vt_insert_lines(uint8_t count)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, count);
+	uart_tx('L');
+}
+
+
+/** Delete lines from the current line down */
+void vt_delete_lines(uint8_t count)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, count);
+	uart_tx('M');
+}
+
+
+/** Insert empty characters at cursor */
+void vt_insert_chars(uint8_t count)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, count);
+	uart_tx('@');
+}
+
+
+/** Delete characters at cursor */
+void vt_delete_chars(uint8_t count)
+{
+	uart_tx(27);
+	uart_tx('[');
+	put_u8(uart, count);
+	uart_tx('P');
+}
+
+
+void vt_clear()
+{
+	uart_puts_P(PSTR("\x1B[2J"));
+}
+
+
+void vt_erase_forth()
+{
+	uart_puts_P(PSTR("\x1B[K"));
+}
+
+
+void vt_erase_back()
+{
+	uart_puts_P(PSTR("\x1B[1K"));
+}
+
+
+void vt_erase_line()
+{
+	uart_puts_P(PSTR("\x1B[2K"));
+}
+
+
+void vt_erase_above()
+{
+	uart_puts_P(PSTR("\x1B[1J"));
+}
+
+void vt_erase_below()
+{
+	uart_puts_P(PSTR("\x1B[J"));
+}
+
+
+void vt_home()
+{
+	uart_puts_P(PSTR("\x1B[H"));
+}
+
+
+/** Initialize helper variables */
+void vt_init()
+{
+	vt_reset();
+}
+
+
+/** Reset state and clear screen */
+void vt_reset()
+{
+	// reset color and attributes
+	vt_color_reset();
+	vt_attr_reset();
+	vt_scroll_reset();
+
+	// clear screen
+	vt_clear();
+
+	// go to top left
+	vt_home();
+
+	// overwrite saved state
+	vt_save();
+}
+
+
+
+// Assigned keyhandler
+void (*_vt_kh)(uint8_t, bool) = NULL;
+
+/** Assign a key handler (later used with vt_handle_key) */
+void vt_set_key_handler(void (*handler)(uint8_t, bool))
+{
+	_vt_kh = handler;
+}
+
+
+// state machine states
+typedef enum {
+	GROUND = 0,
+	ESC = 1,
+	BR = 2,
+	O = 3,
+	WAITING_TILDE = 4
+} KSTATE;
+
+// code received before started to wait for a tilde
+uint8_t _before_wtilde;
+// current state
+KSTATE _kstate = GROUND;
+
+
+
+void _vt_kh_abort()
+{
+	switch (_kstate) {
+		case ESC:
+			_vt_kh(VK_ESC, true);
+			break;
+
+		case BR:
+			_vt_kh(VK_ESC, true);
+			_vt_kh('[', false);
+			break;
+
+		case O:
+			_vt_kh(VK_ESC, true);
+			_vt_kh('O', false);
+			break;
+
+		case WAITING_TILDE:
+			_vt_kh(VK_ESC, true);
+			_vt_kh('[', false);
+			vt_handle_key(_before_wtilde);
+			break;
+
+		case GROUND:
+			// nop
+			break;
+	}
+
+	_kstate = GROUND;
+}
+
+
+/**
+ * Handle a key received over UART
+ * Takes care of multi-byte keys and translates them to special
+ * constants.
+ */
+void vt_handle_key(uint8_t c)
+{
+	if (_vt_kh == NULL) return;
+
+	switch (_kstate) {
+		case GROUND:
+			switch (c) {
+				case 27:
+					_kstate = ESC;
+					break;
+
+				case VK_ENTER:
+				case VK_TAB:
+				case VK_BACKSPACE:
+					_vt_kh(c, true);
+					return;
+
+				default:
+					_vt_kh(c, false);
+					return;
+			}
+
+			break;  // continue to next char
+
+		case ESC:
+			switch (c) {
+				case '[':
+					_kstate = BR;
+					break; // continue to next char
+
+				case 'O':
+					_kstate = O;
+					break; // continue to next char
+
+				default:
+					// bad code
+					_vt_kh_abort();
+					vt_handle_key(c);
+					return;
+			}
+			break;
+
+		case BR:
+			switch (c) {
+				// arrows
+				case 65:
+				case 66:
+				case 67:
+				case 68:
+					_vt_kh(c, true);
+					_kstate = GROUND;
+					return;
+
+				// ins del pgup pgdn
+				case 50:
+				case 51:
+				case 53:
+				case 54:
+					// wait for terminating tilde
+					_before_wtilde = c;
+					_kstate = WAITING_TILDE;
+					break; // continue to next char
+
+				// bad key
+				default:
+					_vt_kh_abort();
+					vt_handle_key(c);
+					return;
+			}
+			break;
+
+		case O:
+			switch (c) {
+				// F keys
+				case 80:
+				case 81:
+				case 82:
+				case 83:
+				// home, end
+				case 72:
+				case 70:
+					_vt_kh(c, true);
+					_kstate = GROUND;
+					return;
+
+				// bad key
+				default:
+					_vt_kh_abort();
+					vt_handle_key(c);
+					return;
+			}
+
+		case WAITING_TILDE:
+			if (c != '~') {
+				_vt_kh_abort();
+				vt_handle_key(c);
+				return;
+			} else {
+				_vt_kh(_before_wtilde, true);
+				_kstate = GROUND;
+				return;
+			}
+	}
+
+	// wait for next key
+	if (_kstate != GROUND) {
+		_delay_ms(2);
+		if (!uart_rx_ready()) {
+			// abort receiving
+			_vt_kh_abort();
+
+		} else {
+			vt_handle_key(uart_rx());
+		}
+	}
+}
diff --git a/projects/test_onewire/lib/uart.h b/projects/test_onewire/lib/uart.h
new file mode 100644
index 0000000..13b0775
--- /dev/null
+++ b/projects/test_onewire/lib/uart.h
@@ -0,0 +1,253 @@
+#pragma once
+
+//
+// Utilities for UART communication.
+//
+// First, init uart with desired baud rate using uart_init(baud).
+// Then enable interrupts you want with uart_isr_XXX().
+//
+
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "stream.h"
+
+// Shared UART stream object
+// Can be used with functions from stream.h once UART is initialized
+extern STREAM* uart;
+
+/** Init UART for given baudrate */
+void _uart_init_do(uint16_t ubrr); // internal, needed for the macro.
+#define uart_init(baud) _uart_init_do(F_CPU / 16 / (baud) - 1)
+
+/** Check if there's a byte in the RX register */
+#define uart_rx_ready() (0 != (UCSR0A & (1 << RXC0)))
+
+/** Check if transmission of everything is done */
+#define uart_tx_ready() (0 != (UCSR0A & (1 << UDRE0)))
+
+
+
+// Enable UART interrupts
+
+/** Enable or disable RX ISR */
+void uart_isr_rx(bool enable);
+
+/** Enable or disable TX ISR (1 byte is sent) */
+void uart_isr_tx(bool enable);
+
+/** Enable or disable DRE ISR (all is sent) */
+void uart_isr_dre(bool enable);
+
+
+
+// Basic IO
+
+/** Receive one byte over UART */
+uint8_t uart_rx();
+
+/** Send byte over UART */
+#define uart_putc(data) uart_tx((data))
+void uart_tx(uint8_t data);
+
+/** Clear receive buffer */
+void uart_flush();
+
+
+
+// Strings
+
+/** Send string over UART */
+void uart_puts(const char* str);
+
+/** Send progmem string over UART */
+void uart_puts_P(const char* str);
+
+
+
+//
+// ANSI / VT100 utilities for UART
+//
+// To use this, first call uart_init(baud) and vt_init()
+// To print stuff on the screen, use uart_puts() etc from uart.h
+//
+
+
+// INIT
+
+/** Initialize helper variables */
+void vt_init();
+
+/** Reset state and clear screen */
+void vt_reset();
+
+
+
+// CURSOR MOVE
+
+/** Move cursor to top left corner */
+void vt_home();
+
+/** Jump to a location on the screen */
+void vt_goto(uint8_t x, uint8_t y);
+
+/** Jump to given X, keep Y */
+void vt_goto_x(uint8_t x);
+
+/** Jump to given Y, keep X */
+void vt_goto_y(uint8_t y);
+
+/** Move cursor relative to current location */
+void vt_move(int8_t x, int8_t y);
+
+/** Move cursor horizontally */
+void vt_move_x(int8_t x);
+
+/** Move cursor vertically */
+void vt_move_y(int8_t y);
+
+/** Move cursor up y cells */
+void vt_up(uint8_t y);
+
+/** Move cursor down y cells */
+void vt_down(uint8_t y);
+
+/** Move cursor left x cells */
+void vt_left(uint8_t x);
+
+/** Move cursor right x cells */
+void vt_right(uint8_t x);
+
+
+
+// SCROLLING
+
+/** Scroll y lines down (like up/down, but moves window if needed) */
+void vt_scroll(int8_t down);
+
+/** Set scrolling region (lines) */
+void vt_scroll_set(uint8_t from, uint8_t to);
+
+
+/** Sets scrolling region to the entire screen. */
+void vt_scroll_reset();
+
+
+// COLOR
+
+#define VT_BLACK 0
+#define VT_RED 1
+#define VT_GREEN 2
+#define VT_YELLOW 3
+#define VT_BLUE 4
+#define VT_MAGENTA 5
+#define VT_CYAN 6
+#define VT_WHITE 7
+
+/** Set color 0..7 */
+void vt_color(uint8_t fg, uint8_t bg);
+
+/** Set FG color 0..7 */
+void vt_color_fg(uint8_t fg);
+
+/** Set BG color 0..7 */
+void vt_color_bg(uint8_t bg);
+
+/** Set color to white on black */
+void vt_color_reset();
+
+
+
+// STYLES
+
+#define VT_BOLD 1
+#define VT_UNDERLINE 2
+#define VT_BLINK 4
+#define VT_REVERSE 8
+#define VT_ITALIC 16
+#define VT_FAINT 32
+
+/** Enable or disable a text attribute */
+void vt_attr(uint8_t attribute, bool on);
+
+/** Disable all text attributes (excluding color) */
+void vt_attr_reset();
+
+
+
+// SAVE & RESTORE
+
+/** Save cursor position & text attributes */
+void vt_save();
+
+/** Restore cursor to saved values */
+void vt_restore();
+
+
+
+// MODIFY
+
+
+/** Insert blank lines febore the current line */
+void vt_insert_lines(uint8_t count);
+
+/** Delete lines from the current line down */
+void vt_delete_lines(uint8_t count);
+
+/** Insert empty characters at cursor */
+void vt_insert_chars(uint8_t count);
+
+/** Delete characters at cursor */
+void vt_delete_chars(uint8_t count);
+
+
+
+// ERASING
+
+/** Clear the screen */
+void vt_clear();
+
+/** Erase to the end of line */
+void vt_erase_forth();
+
+/** Erase line to cursor */
+void vt_erase_back();
+
+/** Erase entire line */
+void vt_erase_line();
+
+/** Erase screen below the line */
+void vt_erase_above();
+
+/** Erase screen above the line */
+void vt_erase_below();
+
+
+
+// KEY HANDLER
+
+// Special keys from key handler
+#define VK_LEFT   68
+#define VK_RIGHT  67
+#define VK_UP     65
+#define VK_DOWN   66
+#define VK_DELETE 51
+#define VK_INSERT 50
+#define VK_PGUP   53
+#define VK_PGDN   54
+#define VK_HOME   72
+#define VK_END    70
+#define VK_F1 80
+#define VK_F2 81
+#define VK_F3 82
+#define VK_F4 83
+#define VK_BACKSPACE  8
+#define VK_TAB        9
+#define VK_ENTER     13
+#define VK_ESC       27
+
+void vt_handle_key(uint8_t c);
+void vt_set_key_handler(void (*handler)(uint8_t, bool));
diff --git a/projects/onewire_test/main.c b/projects/test_onewire/main.c
similarity index 100%
rename from projects/onewire_test/main.c
rename to projects/test_onewire/main.c