diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..75e81adbf3294f98fc38ee70d420edd2a7381810
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "hackrf"]
+	path = hackrf
+	url = https://github.com/jboone/hackrf.git
diff --git a/.travis.yml b/.travis.yml
index 752fe767d27ad361128b2582c4f9b5a16b4bcf17..83900662df6061afaaa321f9e7b469bf2354f274 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,16 @@ matrix:
       cache: apt
       dist: xenial
 
+env:
+  global:
+    - PROJECT_NAME=PortaPack-HAVOC
+    - SHORT_COMMIT_HASH=`git rev-parse --short HEAD`
+    - VERSION_STRING=nightly-$SHORT_COMMIT_HASH
+    - BUILD_DATE="`date +%Y-%m-%d`"
+    - BUILD_NAME="$PROJECT_NAME-$BUILD_DATE-$SHORT_COMMIT_HASH"
+    - ARTEFACT_BASE=$TRAVIS_BUILD_DIR/artefacts/
+    - ARTEFACT_PATH=$ARTEFACT_BASE/$BUILD_NAME
+
 notifications:
   irc:
     channels:
@@ -16,7 +26,7 @@ notifications:
       - "Change view : %{compare_url}"
       - "Build details : %{build_url}"
       # TODO: The "build_number.1" in this URL is almost certainly wrong, but correct value not available from Travis?
-      - "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/build/firmware/portapack-h1-firmware-%{commit}.tar.bz2"
+      - "Firmware download : https://jboone.github.io/portapack-havoc-nightly/"
 
 before_install:
   - sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y
@@ -25,17 +35,39 @@ before_install:
 
 script:
   # TODO: Introduce top-level Makefile, this is lame.
+  - sed -e "s/\#set(VERSION.*/set(VERSION \"$VERSION_STRING\")/" -i".bak" CMakeLists.txt
   - mkdir build/
   - pushd build/
   - cmake ..
-  - make release
+  - make firmware
   - popd
 
+after_success:
+  - mkdir -p $ARTEFACT_PATH
+  # Copy firmware to firmware-bin directory
+  - cd $TRAVIS_BUILD_DIR/build
+  - cp firmware/portapack-h1-havoc.bin $ARTEFACT_PATH/
+  - cp hackrf/firmware/hackrf_usb/hackrf_usb.dfu $ARTEFACT_PATH/
+  - cd $TRAVIS_BUILD_DIR
+  - cp LICENSE $ARTEFACT_PATH/
+  # Build the archive
+  - cd $ARTEFACT_BASE
+  - tar -cJvf $ARTEFACT_BASE/$BUILD_NAME.tar.xz $BUILD_NAME
+  - md5sum --binary $BUILD_NAME.tar.xz >MD5SUMS
+  - sha256sum --binary $BUILD_NAME.tar.xz >SHA256SUMS
+
 addons:
   apt:
     packages:
+      - coreutils
+      - tar
+      - sed
+      - cmake
       - dfu-util
-  artifacts:
-    paths:
-      - $(ls build/firmware/portapack-h1-havoc-*.tar.bz2 | tr "\n" ":")
-      - $(ls build/firmware/portapack-h1-havoc-*.zip | tr "\n" ":")
+
+deploy:
+  provider: script
+  skip-cleanup: true
+  script: bash $TRAVIS_BUILD_DIR/tools/deploy-nightly.sh
+  on:
+    branch: master
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ac13907e93af81909962040ef8b19f43d2062308..cf8b5706a95dd8a01b648a18514b594ae23dea1f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,21 +25,37 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex
 
 project(portapack-h1)
 
-execute_process(
-	COMMAND git log -n 1 --format=%h
-	WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
-	RESULT_VARIABLE GIT_REVISION_FOUND
-	ERROR_QUIET
-	OUTPUT_VARIABLE GIT_REVISION
-	OUTPUT_STRIP_TRAILING_WHITESPACE
-)
-if (GIT_REVISION_FOUND)
-	set(VERSION "unknown")
-else (GIT_REVISION_FOUND)
-	set(VERSION ${GIT_REVISION})
-endif (GIT_REVISION_FOUND)
+#set(VERSION "")
+if (NOT DEFINED VERSION)
+	execute_process(
+		COMMAND git log -n 1 --format=%h
+		WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+		RESULT_VARIABLE GIT_VERSION_FOUND
+		ERROR_QUIET
+		OUTPUT_VARIABLE GIT_VERSION
+		OUTPUT_STRIP_TRAILING_WHITESPACE
+	)
+	if (GIT_VERSION_FOUND)
+		set(VERSION "unknown")
+	else (GIT_VERSION_FOUND)
+		set(VERSION "local-${GIT_VERSION}")
+	endif (GIT_VERSION_FOUND)
+endif()
 
 set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
 set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
 
+add_subdirectory(hackrf/firmware/hackrf_usb)
+
+set(HACKRF_FIRMWARE_DFU_FILENAME hackrf_usb.dfu)
+set(HACKRF_FIRMWARE_BIN_FILENAME hackrf_usb_ram.bin)
+set(HACKRF_CPLD_XSVF_FILENAME default.xsvf)
+
+set(HACKRF_PATH ${CMAKE_CURRENT_LIST_DIR}/hackrf)
+set(HACKRF_CPLD_TOOL ${HACKRF_PATH}/firmware/tools/cpld_bitstream.py)
+set(HACKRF_CPLD_XSVF_PATH ${HACKRF_PATH}/firmware/cpld/sgpio_if/${HACKRF_CPLD_XSVF_FILENAME})
+
+set(HACKRF_FIRMWARE_DFU_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_DFU_FILENAME})
+set(HACKRF_FIRMWARE_BIN_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_BIN_FILENAME})
+
 add_subdirectory(firmware)
diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt
index bcfd9ed2d936d26a7822c4e17a5eb0d3eb5399c4..fc36b090678860b4fe10f134c1b211b589d4380e 100644
--- a/firmware/CMakeLists.txt
+++ b/firmware/CMakeLists.txt
@@ -26,37 +26,12 @@ set(CHIBIOS ${PROJECT_SOURCE_DIR}/chibios)
 set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
 
 set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
-set(STRIP_DFU ${PROJECT_SOURCE_DIR}/tools/strip_dfu.py)
 set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
 set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
 
 set(FIRMWARE_NAME portapack-h1-havoc)
 set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
 
-include(ExternalProject)
-find_program(MAKE_EXE NAMES gmake nmake make)
-ExternalProject_Add(hackrf
-	GIT_REPOSITORY https://github.com/jboone/hackrf.git
-	# SOURCE_SUBDIR firmware
-	# SOURCE_SUBDIR isn't available in CMake 3.5 (Ubuntu 16.04 LTS), so the following is a work-around:
-	CONFIGURE_COMMAND ${CMAKE_COMMAND} "-GUnix Makefiles" ../hackrf/firmware
-	BUILD_COMMAND ${MAKE_EXE} hackrf_usb.dfu
-	INSTALL_COMMAND ""
-)
-ExternalProject_Get_Property(hackrf SOURCE_DIR)
-ExternalProject_Get_Property(hackrf BINARY_DIR)
-set(hackrf_SOURCE_DIR ${SOURCE_DIR})
-set(hackrf_BINARY_DIR ${BINARY_DIR})
-
-set(HACKRF_FIRMWARE_DFU_FILENAME hackrf_usb.dfu)
-set(HACKRF_FIRMWARE_BIN_FILENAME hackrf_usb.bin)
-
-set(HACKRF_FIRMWARE_DFU_IMAGE ${hackrf_BINARY_DIR}/hackrf_usb/${HACKRF_FIRMWARE_DFU_FILENAME})
-set(HACKRF_FIRMWARE_BIN_IMAGE ${hackrf_BINARY_DIR}/hackrf_usb/${HACKRF_FIRMWARE_BIN_FILENAME})
-set(HACKRF_CPLD_TOOL ${hackrf_SOURCE_DIR}/firmware/tools/cpld_bitstream.py)
-set(HACKRF_CPLD_XSVF_FILENAME default.xsvf)
-set(HACKRF_CPLD_XSVF_PATH ${hackrf_SOURCE_DIR}/firmware/cpld/sgpio_if/${HACKRF_CPLD_XSVF_FILENAME})
-
 add_subdirectory(application)
 add_subdirectory(baseband)
 
@@ -70,8 +45,8 @@ add_custom_command(
 )
 
 add_custom_target(
-	firmware
-	DEPENDS ${FIRMWARE_FILENAME}
+	firmware ALL
+	DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
 )
 
 add_custom_target(
@@ -79,22 +54,29 @@ add_custom_target(
 	COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE}
 	COMMAND sleep 3s
 	COMMAND hackrf_spiflash -w ${FIRMWARE_FILENAME}
-	DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_FILENAME}
+	DEPENDS ${FIRMWARE_FILENAME}
 )
 
 # TODO: Bad hack to fix location of LICENSE file for tar.
 add_custom_command(
-	OUTPUT ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_NAME}-${GIT_REVISION}.zip
+	OUTPUT ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
 	COMMAND cp ${LICENSE_PATH} LICENSE
 	COMMAND cp ${HACKRF_FIRMWARE_DFU_IMAGE} ${HACKRF_FIRMWARE_DFU_FILENAME}
-	COMMAND tar -c -j -f ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME} LICENSE
-	COMMAND zip -9 -q ${FIRMWARE_NAME}-${GIT_REVISION}.zip ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME} LICENSE
+	COMMAND tar -c -j -f ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME} LICENSE
+	COMMAND zip -9 -q ${FIRMWARE_NAME}-${VERSION}.zip ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME} LICENSE
 	COMMAND rm -f LICENSE ${HACKRF_FIRMWARE_DFU_FILENAME}
-	DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_FILENAME} ${LICENSE_PATH} ${HACKRF_FIRMWARE_DFU_IMAGE}
+	DEPENDS ${FIRMWARE_FILENAME} ${LICENSE_PATH} ${HACKRF_FIRMWARE_DFU_FILENAME}
 	VERBATIM
 )
 
+add_custom_command(
+	OUTPUT MD5SUMS SHA256SUMS
+	COMMAND md5sum --binary ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip >MD5SUMS
+	COMMAND sha256sum --binary ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip >SHA256SUMS
+	DEPENDS ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
+)
+
 add_custom_target(
 	release
-	DEPENDS ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_NAME}-${GIT_REVISION}.zip
+	DEPENDS MD5SUMS SHA256SUMS
 )
diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt
index a2c74450359e5f769e56ab4b6e98190324fd24d0..91fec2fa7e40b605f77bc158caaffde00dfd805d 100644
--- a/firmware/application/CMakeLists.txt
+++ b/firmware/application/CMakeLists.txt
@@ -206,6 +206,7 @@ set(CPPSRC
 	ui/ui_font_fixed_8x16.cpp
 	ui/ui_geomap.cpp
 	ui/ui_menu.cpp
+	ui/ui_btngrid.cpp
 	ui/ui_receiver.cpp
 	ui/ui_rssi.cpp
 	ui/ui_spectrum.cpp
@@ -346,8 +347,7 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
 # List all default C defines here, like -D_DEBUG=1
 # TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
 # NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
-
-set(DDEFS -DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -DGIT_REVISION=\"${GIT_REVISION}\")
+set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"'")
 
 # List all default ASM defines here, like -D_DEBUG=1
 set(DADEFS)
@@ -408,7 +408,7 @@ add_custom_command(
 add_custom_command(
 	OUTPUT ${HACKRF_CPLD_DATA_CPP}
 	COMMAND ${HACKRF_CPLD_TOOL} --xsvf ${HACKRF_CPLD_XSVF_PATH} --portapack-data ${HACKRF_CPLD_DATA_CPP}
-	DEPENDS ${HACKRF_CPLD_TOOL} ${HACKRF_CPLD_XSVF_PATH} hackrf
+	DEPENDS ${HACKRF_CPLD_TOOL} ${HACKRF_CPLD_XSVF_PATH}
 )
 
 add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
diff --git a/firmware/application/apps/ui_about.hpp b/firmware/application/apps/ui_about.hpp
index fd8a1cbc609cf38135bdfd41b77ae8a21367cce8..02987f7a731fcc640a6edc69338f6555d7d4728c 100644
--- a/firmware/application/apps/ui_about.hpp
+++ b/firmware/application/apps/ui_about.hpp
@@ -75,7 +75,7 @@ private:
 	const credits_t credits[25] = {
 		//           012345678901234567890123456789
 		{ 60,		"PortaPack|HAVOC",					0 },
-		{ 7 * 8,	"Git hash " GIT_REVISION,			16 },
+		{ 4 * 8,	    "Version " VERSION_STRING,		16 },
 		{ 11 * 8,	           "Gurus  J. Boone",		0 },
 		{ 18 * 8,	                  "M. Ossmann",		16 },
 		{ 11 * 8,	           "HAVOC  Furrtek",		16 },
diff --git a/firmware/application/apps/ui_about_demo.hpp b/firmware/application/apps/ui_about_demo.hpp
index 080c61959a008c31ecef65722bcd6f82461e7c69..1292b789a31b0f400ae05d0da2d68288c357d768 100644
--- a/firmware/application/apps/ui_about_demo.hpp
+++ b/firmware/application/apps/ui_about_demo.hpp
@@ -131,7 +131,7 @@ private:
 
 	Text text_firmware {
 		{ 0, 236, 240, 16 },
-		"Git Commit Hash        " GIT_REVISION,
+		"Version   " VERSION_STRING,
 	};
 
 	Text text_cpld_hackrf {
diff --git a/firmware/application/apps/ui_freqman.cpp b/firmware/application/apps/ui_freqman.cpp
index 1813eb812bf11bd50c1864cd42da37d82bcae3e1..9d14f7a7acd8a515bc4df965d69860caf3f6a50a 100644
--- a/firmware/application/apps/ui_freqman.cpp
+++ b/firmware/application/apps/ui_freqman.cpp
@@ -29,6 +29,8 @@ using namespace portapack;
 
 namespace ui {
 
+static int32_t last_category_id { 0 };
+
 FreqManBaseView::FreqManBaseView(
 	NavigationView& nav
 ) : nav_ (nav)
@@ -80,7 +82,7 @@ void FreqManBaseView::populate_categories() {
 	});
 	
 	options_category.set_options(categories);
-	options_category.set_selected_index(0);
+	options_category.set_selected_index(last_category_id);
 	
 	options_category.on_change = [this](size_t category_id, int32_t) {
 		if (on_change_category)
@@ -92,7 +94,7 @@ void FreqManBaseView::change_category(int32_t category_id) {
 	
 	if (!file_list.size()) return;
 	
-	current_category_id = category_id;
+	last_category_id = current_category_id = category_id;
 	
 	if (!load_freqman_file(file_list[categories[current_category_id].second], database))
 		error_ = ERROR_ACCESS;
@@ -219,7 +221,7 @@ FrequencyLoadView::FrequencyLoadView(
 		nav.pop();
 	};
 	
-	change_category(0);
+	change_category(last_category_id);
 	refresh_list();
 	
 	on_select_frequency = [&nav, this]() {
@@ -308,7 +310,7 @@ FrequencyManagerView::FrequencyManagerView(
 		nav.pop();
 	};
 	
-	change_category(0);
+	change_category(last_category_id);
 	refresh_list();
 	
 	on_select_frequency = [this]() {
diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp
index 5f854ef34059c761f6acc320e37a948c0b7a4416..02b36370b5e272eb02cb226c6842c6d7522cc0c1 100644
--- a/firmware/application/apps/ui_scanner.cpp
+++ b/firmware/application/apps/ui_scanner.cpp
@@ -78,7 +78,9 @@ void ScannerThread::run() {
 }
 
 void ScannerView::handle_retune(uint32_t i) {
-	text_cycle.set(to_string_dec_uint(i) + "/" + to_string_dec_uint(frequency_list.size()));
+	text_cycle.set(	to_string_dec_uint(i) + "/" +
+					to_string_dec_uint(frequency_list.size()) + " : " +
+					to_string_dec_uint(frequency_list[i]) );
 }
 
 void ScannerView::focus() {
@@ -101,20 +103,46 @@ ScannerView::ScannerView(
 		&field_vga,
 		&field_rf_amp,
 		&field_volume,
+		&field_bw,
 		&field_squelch,
+		&field_wait,
 		//&record_view,
 		&text_cycle,
 		//&waterfall,
 	});
-	
-	// DEBUG
-	frequency_list.push_back(466025000);
-	frequency_list.push_back(466050000);
-	frequency_list.push_back(466075000);
-	frequency_list.push_back(466175000);
-	frequency_list.push_back(466206250);
-	frequency_list.push_back(466231250);
-	
+
+	std::string scanner_file = "SCANNER";
+	if (load_freqman_file(scanner_file, database)) {
+		for(auto& entry : database) {
+			// FIXME
+			if (entry.type == RANGE) {
+				for (uint32_t i=entry.frequency_a; i < entry.frequency_b; i+= 1000000) {
+					frequency_list.push_back(i);
+				}
+			} else {
+				frequency_list.push_back(entry.frequency_a);
+			}
+		}
+	} else {
+		// DEBUG
+		frequency_list.push_back(466025000);
+		frequency_list.push_back(466050000);
+		frequency_list.push_back(466075000);
+		frequency_list.push_back(466175000);
+		frequency_list.push_back(466206250);
+		frequency_list.push_back(466231250);
+	}
+
+	field_bw.set_selected_index(2);
+	field_bw.on_change = [this](size_t n, OptionsField::value_t) {
+		receiver_model.set_nbfm_configuration(n);
+	};
+
+	field_wait.on_change = [this](int32_t v) {
+		wait = v;
+	};
+	field_wait.set_value(5);
+
 	field_squelch.on_change = [this](int32_t v) {
 		squelch = v;
 	};
@@ -134,7 +162,7 @@ ScannerView::ScannerView(
 	receiver_model.set_baseband_bandwidth(1750000);
 	receiver_model.enable();
 	receiver_model.set_squelch_level(0);
-	receiver_model.set_nbfm_configuration(2);	// 16k
+	receiver_model.set_nbfm_configuration(field_bw.selected_index());
 	audio::output::unmute();
 	
 	// TODO: Scanning thread here
@@ -144,11 +172,11 @@ ScannerView::ScannerView(
 void ScannerView::on_statistics_update(const ChannelStatistics& statistics) {
 	int32_t max_db = statistics.max_db;
 	
-	if (timer < 6)
+	if (timer <= wait)
 		timer++;
 	
 	if (max_db < -squelch) {
-		if (timer == 5) {
+		if (timer == wait) {
 			//audio::output::stop();
 			scan_thread->set_scanning(true);
 		}
diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp
index 67cf7a4ab9a67c28ebb60128f7b7958c369f1cb2..52e48e290c3149bc009cc8bc4ce9528c4382831b 100644
--- a/firmware/application/apps/ui_scanner.hpp
+++ b/firmware/application/apps/ui_scanner.hpp
@@ -24,6 +24,7 @@
 
 #include "ui_receiver.hpp"
 #include "ui_font_fixed_8x16.hpp"
+#include "freqman.hpp"
 
 namespace ui {
 
@@ -67,10 +68,12 @@ private:
 	std::vector<rf::Frequency> frequency_list { };
 	int32_t squelch { 0 };
 	uint32_t timer { 0 };
+	uint32_t wait { 0 };
+	freqman_db database { };
 	
 	Labels labels {
 		{ { 0 * 8, 0 * 16 }, "LNA:   VGA:   AMP:  VOL:", Color::light_grey() },
-		{ { 0 * 8, 1 * 16 }, "SQUELCH:  /99", Color::light_grey() },
+		{ { 0 * 8, 1 * 16 }, "BW:    SQUELCH:  /99 WAIT:", Color::light_grey() },
 		{ { 0 * 8, 3 * 16 }, "Work in progress...", Color::light_grey() }
 	};
 	
@@ -93,15 +96,33 @@ private:
 		1,
 		' ',
 	};
-	
+
+	OptionsField field_bw {
+		{ 3 * 8, 1 * 16 },
+		3,
+		{
+			{ "8k5", 0 },
+			{ "11k", 0 },
+			{ "16k", 0 },
+		}
+	};
+
 	NumberField field_squelch {
-		{ 8 * 8, 1 * 16 },
+		{ 15 * 8, 1 * 16 },
 		2,
 		{ 0, 99 },
 		1,
 		' ',
 	};
-	
+
+	NumberField field_wait {
+		{ 26 * 8, 1 * 16 },
+		2,
+		{ 0, 99 },
+		1,
+		' ',
+	};
+
 	Text text_cycle {
 		{ 0, 5 * 16, 240, 16 },
 		"--/--"
diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp
index 7cee4f0c9a51159d5f21e870206e7a15cc12ae6f..3b5747dbbeac6ca08d4254b5b92f733d4eeba1aa 100644
--- a/firmware/application/apps/ui_settings.cpp
+++ b/firmware/application/apps/ui_settings.cpp
@@ -471,15 +471,16 @@ void ModInfoView::focus() {
 
 SettingsMenuView::SettingsMenuView(NavigationView& nav) {
 	add_items({
-		{ "Audio", 			ui::Color::white(), &bitmap_icon_speaker,	[&nav](){ nav.push<SetAudioView>(); } },
-		{ "Radio",			ui::Color::white(), nullptr,	[&nav](){ nav.push<SetRadioView>(); } },
-		{ "UI", 			ui::Color::white(), nullptr,	[&nav](){ nav.push<SetUIView>(); } },
-		//{ "SD card modules", ui::Color::white(), [&nav](){ nav.push<ModInfoView>(); } },
-		{ "Date/Time",		ui::Color::white(), nullptr,	[&nav](){ nav.push<SetDateTimeView>(); } },
-		{ "Touch screen",	ui::Color::white(), nullptr,	[&nav](){ nav.push<TouchCalibrationView>(); } },
-		{ "Play dead",		ui::Color::white(), &bitmap_icon_playdead,	[&nav](){ nav.push<SetPlayDeadView>(); } }
+		//{ "..", 				ui::Color::light_grey(), &bitmap_icon_previous,				[&nav](){ nav.pop(); } },
+		{ "Audio", 			ui::Color::dark_cyan(), &bitmap_icon_speaker,	[&nav](){ nav.push<SetAudioView>(); } },
+		{ "Radio",			ui::Color::dark_cyan(), nullptr,	[&nav](){ nav.push<SetRadioView>(); } },
+		{ "UI", 			ui::Color::dark_cyan(), nullptr,	[&nav](){ nav.push<SetUIView>(); } },
+		//{ "SD card modules", ui::Color::dark_cyan(), [&nav](){ nav.push<ModInfoView>(); } },
+		{ "Date/Time",		ui::Color::dark_cyan(), nullptr,	[&nav](){ nav.push<SetDateTimeView>(); } },
+		{ "Touch screen",	ui::Color::dark_cyan(), nullptr,	[&nav](){ nav.push<TouchCalibrationView>(); } },
+		{ "Play dead",		ui::Color::dark_cyan(), &bitmap_icon_playdead,	[&nav](){ nav.push<SetPlayDeadView>(); } }
 	});
-	on_left = [&nav](){ nav.pop(); };
+	set_max_rows(2); // allow wider buttons
 }
 
 } /* namespace ui */
diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp
index 39ab959793f2bd4a8bcc7e3093d2466abe3a657b..e4084ad32fe241180c9809c13bfa7943ead4f813 100644
--- a/firmware/application/apps/ui_settings.hpp
+++ b/firmware/application/apps/ui_settings.hpp
@@ -356,7 +356,7 @@ private:
 	};
 };*/
 
-class SettingsMenuView : public MenuView {
+class SettingsMenuView : public BtnGridView {
 public:
 	SettingsMenuView(NavigationView& nav);
 	
diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp
index f1f6eb2814da67f1a310fb7c8b74c6b79ceb7f4f..088cf17fb44ec09dbea427999abe27f955dad6da 100644
--- a/firmware/application/clock_manager.cpp
+++ b/firmware/application/clock_manager.cpp
@@ -181,8 +181,8 @@ constexpr ClockControls si5351_clock_control_common { {
 	{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
 	{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
 	{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
-	{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
-	{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
+	{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
+	{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
 	{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
 	{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self,  ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer,    ClockControl::ClockPowerDown::Power_Off },
 } };
diff --git a/firmware/application/ui/ui_alphanum.cpp b/firmware/application/ui/ui_alphanum.cpp
index 7b77eb8df2b4b56b5394c680d4b07d84c6105083..16d2f300a6816ba5413d92cec58eab7d84e604fc 100644
--- a/firmware/application/ui/ui_alphanum.cpp
+++ b/firmware/application/ui/ui_alphanum.cpp
@@ -54,6 +54,10 @@ AlphanumView::AlphanumView(
 
 	n = 0;
 	for (auto& button : buttons) {
+		button.id = n;
+		button.on_highlight = [this](Button& button) {
+			focused_button = button.id;
+		};
 		button.on_select = button_fn;
 		button.set_parent_rect({
 			static_cast<Coord>((n % 5) * (240 / 5)),
@@ -120,4 +124,16 @@ void AlphanumView::on_button(Button& button) {
 	update_text();
 }
 
+bool AlphanumView::on_encoder(const EncoderEvent delta) {
+	focused_button += delta;
+	if (focused_button < 0) {
+		focused_button = buttons.size() - 1;
+	}
+	else if (focused_button >= (int16_t)buttons.size()) {
+		focused_button = 0;
+	}
+	buttons[focused_button].focus();
+	return true;
+}
+
 }
diff --git a/firmware/application/ui/ui_alphanum.hpp b/firmware/application/ui/ui_alphanum.hpp
index 08b6e25081b71eae31bd0409b9c109d43ab57c73..1e2e42acfccb730a1e66db9e3cd9dcfc18aff48d 100644
--- a/firmware/application/ui/ui_alphanum.hpp
+++ b/firmware/application/ui/ui_alphanum.hpp
@@ -41,6 +41,7 @@ public:
 	AlphanumView& operator=(AlphanumView&&) = delete;
 
 	void paint(Painter& painter) override;
+	bool on_encoder(const EncoderEvent delta) override;
 
 private:
 	const char * const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ' .<";
@@ -53,6 +54,7 @@ private:
 		{ "Digit", keys_digit }
 	};
 	
+	int16_t focused_button = 0;
 	uint32_t mode = 0;	// Uppercase
 	
 	void set_mode(const uint32_t new_mode);
diff --git a/firmware/application/ui/ui_btngrid.cpp b/firmware/application/ui/ui_btngrid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c756af4e03660247755dfbbc289b055e053756d
--- /dev/null
+++ b/firmware/application/ui/ui_btngrid.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2016 Furrtek
+ * Copyright (C) 2019 Elia Yehuda (z4ziggy)
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "ui_btngrid.hpp"
+#include "rtc_time.hpp"
+
+namespace ui {
+
+/* BtnGridView **************************************************************/
+
+BtnGridView::BtnGridView(
+	Rect new_parent_rect,
+	bool keep_highlight
+) : keep_highlight { keep_highlight }
+{
+	set_parent_rect(new_parent_rect);
+
+	set_focusable(true);
+
+	signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
+		this->on_tick_second();
+	};
+
+	add_child(&arrow_more);
+	arrow_more.set_focusable(false);
+	arrow_more.set_foreground(Color::black());
+}
+
+BtnGridView::~BtnGridView() {
+	rtc_time::signal_tick_second -= signal_token_tick_second;
+
+	for (auto item : menu_item_views) {
+		delete item;
+	}
+}
+
+void BtnGridView::set_max_rows(int rows) {
+	rows_ = rows;
+}
+
+int BtnGridView::rows() {
+	return rows_;
+}
+
+void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
+	View::set_parent_rect(new_parent_rect);
+
+	displayed_max = (parent_rect().size().height() / button_h);
+	arrow_more.set_parent_rect( { 228, (Coord)(displayed_max * button_h), 8, 8 } );
+	displayed_max *= rows_;
+
+	// TODO: Clean this up :(
+	if (menu_item_views.size()) {
+
+		for (auto item : menu_item_views) {
+			remove_child(item);
+			delete item;
+		}
+		menu_item_views.clear();
+	}
+
+	button_w = 240 / rows_;
+	for (size_t c = 0; c < displayed_max; c++) {
+		auto item = new NewButton {  };
+		menu_item_views.push_back(item);
+		add_child(item);
+
+		item->set_parent_rect({
+			(int)(c % rows_) * button_w,
+			(int)(c / rows_) * button_h,
+			button_w, button_h
+		});
+	}
+
+	update_items();
+}
+
+void BtnGridView::on_tick_second() {
+	if (more && blink)
+		arrow_more.set_foreground(Color::white());
+	else
+		arrow_more.set_foreground(Color::black());
+
+	blink = !blink;
+
+	arrow_more.set_dirty();
+}
+
+void BtnGridView::clear() {
+	menu_items.clear();
+}
+
+void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
+	for (auto item : new_items) {
+		menu_items.push_back(item);
+	}
+	update_items();
+}
+
+void BtnGridView::update_items() {
+	size_t i = 0;
+
+	if ((menu_items.size()) > (displayed_max + offset)) {
+		more = true;
+		blink = true;
+	} else
+		more = false;
+
+	for (NewButton* item : menu_item_views) {
+		if (i >= menu_items.size()) break;
+
+		// Assign item data to NewButtons according to offset
+		item->set_text(menu_items[i + offset].text);
+		item->set_bitmap(menu_items[i + offset].bitmap);
+		item->set_color(menu_items[i + offset].color);
+		item->on_select = menu_items[i + offset].on_select;
+		item->set_dirty();
+
+		i++;
+	}
+}
+
+NewButton* BtnGridView::item_view(size_t index) const {
+	return menu_item_views[index];
+}
+
+bool BtnGridView::set_highlighted(int32_t new_value) {
+	int32_t item_count = (int32_t)menu_items.size();
+
+	if (new_value < 0)
+		return false;
+
+	if (new_value >= item_count)
+		new_value = item_count - 1;
+
+	if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
+		// Shift BtnGridView up
+		highlighted_item = new_value;
+		offset = new_value - displayed_max + rows_;
+		update_items();
+	} else if ((uint32_t)new_value < offset) {
+		// Shift BtnGridView down
+		highlighted_item = new_value;
+		offset = (new_value / rows_) * rows_;
+		update_items();
+	} else {
+		// Just update highlight
+		highlighted_item = new_value;
+		if (visible())
+		  item_view(highlighted_item - offset)->focus();
+	}
+
+	return true;
+}
+
+uint32_t BtnGridView::highlighted_index() {
+	return highlighted_item;
+}
+
+void BtnGridView::on_focus() {
+	item_view(highlighted_item - offset)->focus();
+}
+
+void BtnGridView::on_blur() {
+#if 0
+	if (!keep_highlight)
+		item_view(highlighted_item - offset)->unhighlight();
+#endif
+}
+
+bool BtnGridView::on_key(const KeyEvent key) {
+	switch(key) {
+	case KeyEvent::Up:
+		return set_highlighted(highlighted_item - rows_);
+
+	case KeyEvent::Down:
+		return set_highlighted(highlighted_item + rows_);
+
+	case KeyEvent::Right:
+		return set_highlighted(highlighted_item + 1);
+
+	case KeyEvent::Left:
+		return set_highlighted(highlighted_item - 1);
+
+	case KeyEvent::Select:
+		if( menu_items[highlighted_item].on_select ) {
+			menu_items[highlighted_item].on_select();
+		}
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+bool BtnGridView::on_encoder(const EncoderEvent event) {
+	return set_highlighted(highlighted_item + event);
+}
+
+} /* namespace ui */
diff --git a/firmware/application/ui/ui_btngrid.hpp b/firmware/application/ui/ui_btngrid.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ce14ccacf7bacd793c990b64ebf6fa6179c3c5e
--- /dev/null
+++ b/firmware/application/ui/ui_btngrid.hpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2016 Furrtek
+ * Copyright (C) 2019 Elia Yehuda (z4ziggy)
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __UI_BTNGRID_H__
+#define __UI_BTNGRID_H__
+
+#include "ui.hpp"
+#include "ui_widget.hpp"
+#include "ui_painter.hpp"
+#include "bitmap.hpp"
+#include "signal.hpp"
+
+#include <cstddef>
+#include <string>
+#include <functional>
+
+namespace ui {
+
+struct GridItem {
+	std::string text;
+	ui::Color color;
+	const Bitmap* bitmap;
+	std::function<void(void)> on_select;
+
+	// TODO: Prevent default-constructed GridItems.
+};
+
+class BtnGridView : public View {
+public:
+	BtnGridView(Rect new_parent_rect = { 0, 0, 240, 304 }, bool keep_highlight = false);
+
+	~BtnGridView();
+
+	void add_items(std::initializer_list<GridItem> new_items);
+	void set_max_rows(int rows);
+	int rows();
+	void clear();
+
+	NewButton* item_view(size_t index) const;
+
+	bool set_highlighted(int32_t new_value);
+	uint32_t highlighted_index();
+
+	void set_parent_rect(const Rect new_parent_rect) override;
+	void on_focus() override;
+	void on_blur() override;
+	bool on_key(const KeyEvent event) override;
+	bool on_encoder(const EncoderEvent event) override;
+
+private:
+	int rows_ { 3 };
+	void update_items();
+	void on_tick_second();
+
+	bool keep_highlight { false };
+
+	SignalToken signal_token_tick_second { };
+	std::vector<GridItem> menu_items { };
+	std::vector<NewButton*> menu_item_views { };
+
+	Image arrow_more {
+		{ 228, 320 - 8, 8, 8 },
+		&bitmap_more,
+		Color::white(),
+		Color::black()
+	};
+
+	int button_w = 240 / rows_;
+	static constexpr int button_h = 48;
+	bool blink = false;
+	bool more = false;
+	size_t displayed_max { 0 };
+	size_t highlighted_item { 0 };
+	size_t offset { 0 };
+};
+
+} /* namespace ui */
+
+#endif/*__UI_BTNGRID_H__*/
diff --git a/firmware/application/ui/ui_receiver.cpp b/firmware/application/ui/ui_receiver.cpp
index db4d059d27699149f66b6e7d3088699c9b56b93a..9889bbcbb4854aab7e7895912fc60c58a3dc1293 100644
--- a/firmware/application/ui/ui_receiver.cpp
+++ b/firmware/application/ui/ui_receiver.cpp
@@ -110,6 +110,18 @@ rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
 
 /* FrequencyKeypadView ***************************************************/
 
+bool FrequencyKeypadView::on_encoder(const EncoderEvent delta) {
+	focused_button += delta;
+	if (focused_button < 0) {
+		focused_button = buttons.size() - 1;
+	}
+	else if (focused_button >= (int16_t)buttons.size()) {
+		focused_button = 0;
+	}
+	buttons[focused_button].focus();
+	return true;
+}
+
 FrequencyKeypadView::FrequencyKeypadView(
 	NavigationView& nav,
 	const rf::Frequency value
@@ -128,6 +140,10 @@ FrequencyKeypadView::FrequencyKeypadView(
 		const std::string label {
 			key_caps[n]
 		};
+		button.id = n;
+		button.on_highlight = [this](Button& button) {
+			focused_button = button.id;
+		};
 		button.on_select = button_fn;
 		button.set_parent_rect({
 			(n % 3) * button_w,
diff --git a/firmware/application/ui/ui_receiver.hpp b/firmware/application/ui/ui_receiver.hpp
index 716a311430074cbd90d43b305ceb898ab32bf13f..6bad8886b707bfdc27cedf597b59b8282d409f5e 100644
--- a/firmware/application/ui/ui_receiver.hpp
+++ b/firmware/application/ui/ui_receiver.hpp
@@ -185,8 +185,10 @@ public:
 
 	rf::Frequency value() const;
 	void set_value(const rf::Frequency new_value);
+	bool on_encoder(const EncoderEvent delta) override;
 
 private:
+	int16_t focused_button = 0;
 	static constexpr int button_w = 240 / 3;
 	static constexpr int button_h = 48;
 
diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp
index 504ce9d6999ae019d9bc8ec570051ff24a6026a0..4c574c6301ba3ce4213d3a47592ca07c18ee9825 100644
--- a/firmware/application/ui_navigation.cpp
+++ b/firmware/application/ui_navigation.cpp
@@ -342,23 +342,23 @@ void NavigationView::focus() {
 
 ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
 	add_items({
-		{ "ADS-B: Planes", 			ui::Color::green(),	&bitmap_icon_adsb,	[&nav](){ nav.replace<ADSBRxView>(); }, },
-		{ "ACARS: Planes", 			ui::Color::yellow(),&bitmap_icon_adsb,	[&nav](){ nav.replace<ACARSAppView>(); }, },
-		{ "AIS:   Boats", 			ui::Color::green(),	&bitmap_icon_ais,	[&nav](){ nav.replace<AISAppView>(); } },
-		{ "AFSK", 					ui::Color::yellow(),&bitmap_icon_receivers,	[&nav](){ nav.replace<AFSKRxView>(); } },
-		{ "Audio", 					ui::Color::green(),	&bitmap_icon_speaker,	[&nav](){ nav.replace<AnalogAudioView>(); } },
-		{ "ERT:   Utility Meters", 	ui::Color::green(), &bitmap_icon_ert,	[&nav](){ nav.replace<ERTAppView>(); } },
-		{ "POCSAG", 				ui::Color::green(),	&bitmap_icon_pocsag,	[&nav](){ nav.replace<POCSAGAppView>(); } },
-		{ "Radiosondes", 			ui::Color::yellow(),&bitmap_icon_sonde,	[&nav](){ nav.replace<SondeView>(); } },
-		{ "TPMS:  Cars", 			ui::Color::green(),	&bitmap_icon_tpms,	[&nav](){ nav.replace<TPMSAppView>(); } },
-		{ "APRS", 					ui::Color::grey(),	&bitmap_icon_aprs,	[&nav](){ nav.replace<NotImplementedView>(); } },
-		{ "DMR framing", 			ui::Color::grey(),	&bitmap_icon_dmr,	[&nav](){ nav.replace<NotImplementedView>(); } },
-		{ "SIGFOX", 				ui::Color::grey(),	&bitmap_icon_fox,	[&nav](){ nav.replace<NotImplementedView>(); } }, // SIGFRXView
-		{ "LoRa", 					ui::Color::grey(),	&bitmap_icon_lora,	[&nav](){ nav.replace<NotImplementedView>(); } },
-		{ "SSTV", 					ui::Color::grey(), 	&bitmap_icon_sstv,	[&nav](){ nav.replace<NotImplementedView>(); } },
-		{ "TETRA framing", 			ui::Color::grey(),	&bitmap_icon_tetra,	[&nav](){ nav.replace<NotImplementedView>(); } },
+		//{ "..", 		ui::Color::light_grey(),&bitmap_icon_previous,	[&nav](){ nav.pop(); } },
+		{ "ADS-B", 		ui::Color::green(),		&bitmap_icon_adsb,		[&nav](){ nav.push<ADSBRxView>(); }, },
+		{ "ACARS", 		ui::Color::yellow(),	&bitmap_icon_adsb,		[&nav](){ nav.push<ACARSAppView>(); }, },
+		{ "AIS Boats",	ui::Color::green(),		&bitmap_icon_ais,		[&nav](){ nav.push<AISAppView>(); } },
+		{ "AFSK", 		ui::Color::yellow(),	&bitmap_icon_receivers,	[&nav](){ nav.push<AFSKRxView>(); } },
+		{ "Audio", 		ui::Color::green(),		&bitmap_icon_speaker,	[&nav](){ nav.push<AnalogAudioView>(); } },
+		{ "ERT Meter", 	ui::Color::green(), 	&bitmap_icon_ert,		[&nav](){ nav.push<ERTAppView>(); } },
+		{ "POCSAG", 	ui::Color::green(),		&bitmap_icon_pocsag,	[&nav](){ nav.push<POCSAGAppView>(); } },
+		{ "Radiosnde", 	ui::Color::yellow(),	&bitmap_icon_sonde,		[&nav](){ nav.push<SondeView>(); } },
+		{ "TPMS Cars", 	ui::Color::green(),		&bitmap_icon_tpms,		[&nav](){ nav.push<TPMSAppView>(); } },
+		{ "APRS", 		ui::Color::dark_grey(),	&bitmap_icon_aprs,		[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "DMR", 		ui::Color::dark_grey(),	&bitmap_icon_dmr,		[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "SIGFOX", 	ui::Color::dark_grey(),	&bitmap_icon_fox,		[&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
+		{ "LoRa", 		ui::Color::dark_grey(),	&bitmap_icon_lora,		[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "SSTV", 		ui::Color::dark_grey(), &bitmap_icon_sstv,		[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "TETRA", 		ui::Color::dark_grey(),	&bitmap_icon_tetra,		[&nav](){ nav.push<NotImplementedView>(); } },
 	});
-	on_left = [&nav](){ nav.pop(); };
 	
 	set_highlighted(4);		// Default selection is "Audio"
 }
@@ -367,43 +367,44 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
 
 TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
 	add_items({
-		{ "ADS-B Mode S", 			ui::Color::yellow(), 	&bitmap_icon_adsb,		[&nav](){ nav.push<ADSBTxView>(); } },
+		//{ "..",				ui::Color::light_grey(),&bitmap_icon_previous,	[&nav](){ nav.pop(); } },
+		{ "ADS-B [S]",		ui::Color::yellow(), 	&bitmap_icon_adsb,		[&nav](){ nav.push<ADSBTxView>(); } },
 		{ "APRS", 					ui::Color::orange(),	&bitmap_icon_aprs,		[&nav](){ nav.push<APRSTXView>(); } },
 		{ "BHT Xy/EP", 				ui::Color::green(), 	&bitmap_icon_bht,		[&nav](){ nav.push<BHTView>(); } },
 		{ "Jammer", 				ui::Color::yellow(),	&bitmap_icon_jammer,	[&nav](){ nav.push<JammerView>(); } },
 		{ "Key fob", 				ui::Color::orange(),	&bitmap_icon_keyfob,	[&nav](){ nav.push<KeyfobView>(); } },
 		{ "LGE tool", 				ui::Color::yellow(),	&bitmap_icon_lge,		[&nav](){ nav.push<LGEView>(); } },
-		{ "Microphone", 			ui::Color::green(),		&bitmap_icon_microphone,	[&nav](){ nav.push<MicTXView>(); } },
-		{ "Morse code", 			ui::Color::green(),		&bitmap_icon_morse,		[&nav](){ nav.push<MorseView>(); } },
-		{ "Burger pagers", 			ui::Color::yellow(), 	&bitmap_icon_burger,	[&nav](){ nav.push<CoasterPagerView>(); } },
-		//{ "Nuoptix DTMF timecode", 	ui::Color::green(),		&bitmap_icon_nuoptix,	[&nav](){ nav.push<NuoptixView>(); } },
-		{ "OOK encoders", 			ui::Color::yellow(),	&bitmap_icon_remote,	[&nav](){ nav.push<EncodersView>(); } },
+		{ "Mic",			ui::Color::green(),		&bitmap_icon_microphone,[&nav](){ nav.push<MicTXView>(); } },
+		{ "Morse",			ui::Color::green(),		&bitmap_icon_morse,		[&nav](){ nav.push<MorseView>(); } },
+		{ "BurgerPgr",		ui::Color::yellow(), 	&bitmap_icon_burger,	[&nav](){ nav.push<CoasterPagerView>(); } },
+		//{ "Nuoptix DTMF", 	ui::Color::green(),		&bitmap_icon_nuoptix,	[&nav](){ nav.push<NuoptixView>(); } },
+		{ "OOK",			ui::Color::yellow(),	&bitmap_icon_remote,	[&nav](){ nav.push<EncodersView>(); } },
 		{ "POCSAG", 				ui::Color::green(),		&bitmap_icon_pocsag,	[&nav](){ nav.push<POCSAGTXView>(); } },
 		{ "RDS",					ui::Color::green(),		&bitmap_icon_rds,		[&nav](){ nav.push<RDSView>(); } },
-		{ "Soundboard", 			ui::Color::green(), 	&bitmap_icon_soundboard,	[&nav](){ nav.push<SoundBoardView>(); } },
+		{ "Soundbrd",		ui::Color::green(), 	&bitmap_icon_soundboard,[&nav](){ nav.push<SoundBoardView>(); } },
 		{ "SSTV", 					ui::Color::green(), 	&bitmap_icon_sstv,		[&nav](){ nav.push<SSTVTXView>(); } },
-		{ "TEDI/LCR AFSK", 			ui::Color::yellow(), 	&bitmap_icon_lcr,		[&nav](){ nav.push<LCRView>(); } },
-		{ "TouchTunes remote",		ui::Color::yellow(),	&bitmap_icon_remote,	[&nav](){ nav.push<TouchTunesView>(); } },
-		{ "Custom remote", 			ui::Color::grey(),		&bitmap_icon_remote,	[&nav](){ nav.push<RemoteView>(); } },
+		{ "TEDI/LCR",		ui::Color::yellow(), 	&bitmap_icon_lcr,		[&nav](){ nav.push<LCRView>(); } },
+		{ "TouchTune",		ui::Color::yellow(),	&bitmap_icon_remote,	[&nav](){ nav.push<TouchTunesView>(); } },
+		{ "Remote",			ui::Color::dark_grey(),	&bitmap_icon_remote,	[&nav](){ nav.push<RemoteView>(); } },
 	});
-	on_left = [&nav](){ nav.pop(); };
 }
 
 /* UtilitiesMenuView *****************************************************/
 
 UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
 	add_items({
-		//{ "Test app", 				ui::Color::grey(), 		nullptr,				[&nav](){ nav.push<TestView>(); } },
-		{ "Frequency manager", 		ui::Color::green(), 	&bitmap_icon_freqman,	[&nav](){ nav.push<FrequencyManagerView>(); } },
+		//{ "Test app", 		ui::Color::dark_grey(),	nullptr,				[&nav](){ nav.push<TestView>(); } },
+		//{ "..", 			ui::Color::light_grey(),&bitmap_icon_previous,	[&nav](){ nav.pop(); } },
+		{ "Freq manager",	ui::Color::green(), 	&bitmap_icon_freqman,	[&nav](){ nav.push<FrequencyManagerView>(); } },
 		{ "File manager", 			ui::Color::yellow(),	&bitmap_icon_file,		[&nav](){ nav.push<FileManagerView>(); } },
-		{ "Notepad",				ui::Color::grey(),		&bitmap_icon_notepad,	[&nav](){ nav.push<NotImplementedView>(); } },
-		{ "Signal generator", 		ui::Color::green(), 	&bitmap_icon_cwgen,		[&nav](){ nav.push<SigGenView>(); } },
-		//{ "Tone search", 			ui::Color::grey(), 		nullptr,				[&nav](){ nav.push<ToneSearchView>(); } },
-		{ "Wave file viewer", 		ui::Color::blue(),		nullptr,				[&nav](){ nav.push<ViewWavView>(); } },
-		{ "Whip antenna length",	ui::Color::yellow(),	nullptr,				[&nav](){ nav.push<WhipCalcView>(); } },
+		{ "Notepad",		ui::Color::dark_grey(),	&bitmap_icon_notepad,	[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "Signal gen", 	ui::Color::green(), 	&bitmap_icon_cwgen,		[&nav](){ nav.push<SigGenView>(); } },
+		//{ "Tone search",	ui::Color::dark_grey(), nullptr,				[&nav](){ nav.push<ToneSearchView>(); } },
+		{ "Wave viewer",	ui::Color::blue(),		nullptr,				[&nav](){ nav.push<ViewWavView>(); } },
+		{ "Antenna length",	ui::Color::yellow(),	nullptr,				[&nav](){ nav.push<WhipCalcView>(); } },
 		{ "Wipe SD card",			ui::Color::red(),		nullptr,				[&nav](){ nav.push<WipeSDView>(); } },
 	});
-	on_left = [&nav](){ nav.pop(); };
+	set_max_rows(2); // allow wider buttons
 }
 
 /* SystemMenuView ********************************************************/
@@ -421,17 +422,17 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
 SystemMenuView::SystemMenuView(NavigationView& nav) {
 	add_items({
 		{ "Play dead",				ui::Color::red(),		&bitmap_icon_playdead,	[&nav](){ nav.push<PlayDeadView>(); } },
-		{ "Receivers", 				ui::Color::cyan(),		&bitmap_icon_receivers,	[&nav](){ nav.push<ReceiversMenuView>(); } },
-		{ "Transmitters", 			ui::Color::green(),		&bitmap_icon_transmit,	[&nav](){ nav.push<TransmittersMenuView>(); } },
+		{ "Receivers", 	ui::Color::dark_cyan(),		&bitmap_icon_receivers,	[&nav](){ nav.push<ReceiversMenuView>(); } },
+		{ "Transmit", 	ui::Color::green(),			&bitmap_icon_transmit,	[&nav](){ nav.push<TransmittersMenuView>(); } },
 		{ "Capture",				ui::Color::blue(),		&bitmap_icon_capture,	[&nav](){ nav.push<CaptureAppView>(); } },
 		{ "Replay",					ui::Color::purple(),	&bitmap_icon_replay,	[&nav](){ nav.push<ReplayAppView>(); } },
-		{ "Search/Close call",		ui::Color::yellow(),	&bitmap_icon_closecall,	[&nav](){ nav.push<SearchView>(); } },
-		{ "Scanner",				ui::Color::grey(),		&bitmap_icon_scanner,	[&nav](){ nav.push<ScannerView>(); } },
+		{ "Calls",		ui::Color::yellow(),	    &bitmap_icon_closecall,	[&nav](){ nav.push<SearchView>(); } },
+		{ "Scanner",	ui::Color::orange(),		&bitmap_icon_scanner,	[&nav](){ nav.push<ScannerView>(); } },
 		{ "Utilities",				ui::Color::light_grey(),	&bitmap_icon_utilities,	[&nav](){ nav.push<UtilitiesMenuView>(); } },
-		{ "Settings", 				ui::Color::white(),		&bitmap_icon_setup,		[&nav](){ nav.push<SettingsMenuView>(); } },
-		//{ "Debug", 					ui::Color::white(), nullptr,   				[&nav](){ nav.push<DebugMenuView>(); } },
-		{ "HackRF mode", 			ui::Color::white(),		&bitmap_icon_hackrf,	[this, &nav](){ hackrf_mode(nav); } },
-		{ "About", 					ui::Color::white(),		nullptr,				[&nav](){ nav.push<AboutView>(); } }
+		{ "Settings", 	ui::Color::cyan(),			&bitmap_icon_setup,	  	[&nav](){ nav.push<SettingsMenuView>(); } },
+		//{ "Debug",		ui::Color::cyan(),			nullptr,   				[&nav](){ nav.push<DebugMenuView>(); } },
+		{ "HackRF", 	ui::Color::cyan(),			&bitmap_icon_hackrf,	[this, &nav](){ hackrf_mode(nav); } },
+		{ "About", 		ui::Color::cyan(),			nullptr,				[&nav](){ nav.push<AboutView>(); } }
 	});
 	
 	set_highlighted(1);		// Startup selection is "Receivers"
diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp
index b9c35d5b59c714525fd4e1c58cafb77ce13be5e9..e84f3b0b769b2437c7338e7c69a180a364a0ff94 100644
--- a/firmware/application/ui_navigation.hpp
+++ b/firmware/application/ui_navigation.hpp
@@ -27,6 +27,7 @@
 #include "ui_widget.hpp"
 #include "ui_focus.hpp"
 #include "ui_menu.hpp"
+#include "ui_btngrid.hpp"
 
 #include "ui_rssi.hpp"
 #include "ui_channel.hpp"
@@ -198,8 +199,8 @@ public:
 
 private:
 	Text text_info {
-		{ 76, 284, 20 * 8, 16 },
-		"GIT " GIT_REVISION
+		{ 4*8, 284, 20 * 8, 16 },
+		"Version " VERSION_STRING
 	};
 	
 	Button button_done {
@@ -208,25 +209,25 @@ private:
 	};
 };
 
-class ReceiversMenuView : public MenuView {
+class ReceiversMenuView : public BtnGridView {
 public:
 	ReceiversMenuView(NavigationView& nav);
 	std::string title() const override { return "Receivers"; };
 };
 
-class TransmittersMenuView : public MenuView {
+class TransmittersMenuView : public BtnGridView {
 public:
 	TransmittersMenuView(NavigationView& nav);
 	std::string title() const override { return "Transmitters"; };
 };
 
-class UtilitiesMenuView : public MenuView {
+class UtilitiesMenuView : public BtnGridView {
 public:
 	UtilitiesMenuView(NavigationView& nav);
 	std::string title() const override { return "Utilities"; };	
 };
 
-class SystemMenuView : public MenuView {
+class SystemMenuView : public BtnGridView {
 public:
 	SystemMenuView(NavigationView& nav);
 private:
diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt
index a31bd07e78199fdc16452b659f39d0a64d080409..fd70a03af258c7c8cdb06f2a233150e75a10aa0a 100644
--- a/firmware/baseband/CMakeLists.txt
+++ b/firmware/baseband/CMakeLists.txt
@@ -201,7 +201,7 @@ set(CPPWARN "-Wall -Wextra")
 # List all default C defines here, like -D_DEBUG=1
 # TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
 # NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
-set(DDEFS -DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -DGIT_REVISION=\"${GIT_REVISION}\")
+set(DDEFS "-DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"'")
 
 # List all default ASM defines here, like -D_DEBUG=1
 set(DADEFS)
@@ -469,10 +469,9 @@ DeclareTargets(PWFM wfm_audio)
 ### HackRF "factory" firmware
 
 add_custom_command(
-	OUTPUT hackrf.bin hackrf.img
-	COMMAND ${STRIP_DFU} ${HACKRF_FIRMWARE_DFU_IMAGE} hackrf.bin
-	COMMAND ${MAKE_IMAGE_CHUNK} hackrf.bin HRF1 hackrf.img 98304
-	DEPENDS hackrf ${STRIP_DFU} ${MAKE_IMAGE_CHUNK}
+	OUTPUT hackrf.img
+	COMMAND ${MAKE_IMAGE_CHUNK} ${HACKRF_FIRMWARE_BIN_IMAGE} HRF1 hackrf.img 98304
+	DEPENDS ${HACKRF_FIRMWARE_BIN_FILENAME} ${MAKE_IMAGE_CHUNK}
 	VERBATIM
 )
 
diff --git a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp
index fbfe664b020ff301a1caa53b737d77981ce64ad3..43d297b4dfa3fe4a98687471108aca93de657a82 100755
--- a/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp
+++ b/firmware/chibios-portapack/boards/PORTAPACK_APPLICATION/board.cpp
@@ -170,7 +170,7 @@ const PALConfig pal_default_config = {
             | (1 <<  9) // P7_1:  PortaPack GPIO3_9(IO)
             | (1 <<  8) // P7_0:  PortaPack GPIO3_8(IO)
             | (1 <<  7) // P6_11: VREGMODE
-            | (1 <<  6) // P6_10: EN1V8, 10K PD
+            | (0 <<  6) // P6_10: EN1V8, 10K PD
             | (1 <<  5) // P6_9:  !TX_AMP_PWR, 10K PU
             | (1 <<  4) // P6_5:  HackRF CPLD.TMS(I) (output only when needed, pull-up internal to CPLD when 1V8 present)
             | (1 <<  3) // P6_4:  MIXER_SDATA
@@ -478,8 +478,8 @@ void vaa_power_on(void) {
   /* Combination of pulse duration and duty cycle was arrived at empirically, to keep supply glitching
    * to +/- 0.15V.
    */
-  const uint32_t cycle_period = 128;
-  const uint32_t enable_period = 10;
+  const uint32_t cycle_period = 256;
+  uint32_t enable_period = 2;
   LPC_MCPWM->TC2 = 0;
   LPC_MCPWM->MAT2 = cycle_period - enable_period;
   LPC_MCPWM->LIM2 = cycle_period;
@@ -493,7 +493,11 @@ void vaa_power_on(void) {
   /* Wait until VAA rises to approximately 90% of final voltage. */
   /* Timing assumes we're running immediately after the bootloader: 96 MHz from IRC+PLL1
    */
-  { volatile uint32_t delay = 12000; while(delay--); }
+  while(enable_period < cycle_period) {
+    { volatile uint32_t delay = 2000; while(delay--); }
+    enable_period <<= 1;
+    LPC_MCPWM->MAT2 = cycle_period - enable_period;
+  }
 
   /* Hold !VAA_ENABLE active using a GPIO, so we can reclaim and shut down the MOTOCONPWM peripheral. */
   LPC_GPIO->CLR[2]  = (1 << 9); // !VAA_ENABLE
@@ -635,9 +639,11 @@ extern "C" void __late_init(void) {
  */
 extern "C" void boardInit(void) {
   vaa_power_on();
+  LPC_GPIO->W3[6] = 1;
 }
 
 extern "C" void _default_exit(void) {
+    LPC_GPIO->W3[6] = 0;
     vaa_power_off();
 
     chSysDisable();
diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp
index 02b9bc02c07dd92fb50a61fe25a879c631dcbb00..5e2d7db875fe5170672a20cf89da1e25316b8044 100644
--- a/firmware/common/ui_widget.cpp
+++ b/firmware/common/ui_widget.cpp
@@ -918,6 +918,132 @@ bool Button::on_touch(const TouchEvent event) {
 #endif
 }
 
+/* NewButton ****************************************************************/
+
+NewButton::NewButton(
+	Rect parent_rect,
+	std::string text,
+	const Bitmap* bitmap
+) : Widget { parent_rect },
+	text_ { text },
+	bitmap_ (bitmap)
+{
+	set_focusable(true);
+}
+
+void NewButton::set_text(const std::string value) {
+	text_ = value;
+	set_dirty();
+}
+
+std::string NewButton::text() const {
+	return text_;
+}
+
+void NewButton::set_bitmap(const Bitmap* bitmap) {
+	bitmap_ = bitmap;
+	set_dirty();
+}
+
+const Bitmap* NewButton::bitmap() {
+	return bitmap_;
+}
+
+void NewButton::set_color(Color color) {
+  color_ = color;
+	set_dirty();
+}
+
+ui::Color NewButton::color() {
+	return color_;
+}
+
+void NewButton::paint(Painter& painter) {
+
+	if (!bitmap_ && text_.empty())
+		return;
+
+	Color bg, fg;
+	const auto r = screen_rect();
+
+	if (has_focus() || highlighted()) {
+		bg = style().foreground;
+		fg = Color::black();
+	} else {
+		bg = Color::grey();
+		fg = style().foreground;
+	}
+
+	const Style paint_style = { style().font, bg, fg };
+
+	painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Color::light_grey());
+	painter.draw_rectangle({r.location().x(), r.location().y() + r.size().height() - 1, r.size().width(), 1}, Color::dark_grey());
+	painter.draw_rectangle({r.location().x() + r.size().width() - 1, r.location().y(), 1, r.size().height()}, Color::dark_grey());
+
+	painter.fill_rectangle(
+		{ r.location().x(), r.location().y() + 1, r.size().width() - 1, r.size().height() - 2 },
+		paint_style.background
+	);
+
+	int y = r.location().y();
+	if (bitmap_) {
+		painter.draw_bitmap(
+			{r.location().x() + (r.size().width() / 2) - 8, r.location().y() + 6},
+			*bitmap_,
+			color_, //Color::green(), //fg,
+			bg
+		);
+		y += 10;
+	}
+	const auto label_r = paint_style.font.size_of(text_);
+	painter.draw_string(
+		{ r.location().x() + (r.size().width() - label_r.width()) / 2, y + (r.size().height() - label_r.height()) / 2 },
+		paint_style,
+		text_
+	);
+}
+
+void NewButton::on_focus() {
+	if( on_highlight )
+		on_highlight(*this);
+}
+
+bool NewButton::on_key(const KeyEvent key) {
+	if( key == KeyEvent::Select ) {
+		if( on_select ) {
+			on_select();
+			return true;
+		}
+	} else {
+		if( on_dir ) {
+			return on_dir(*this, key);
+		}
+	}
+
+	return false;
+}
+
+bool NewButton::on_touch(const TouchEvent event) {
+	switch(event.type) {
+	case TouchEvent::Type::Start:
+		set_highlighted(true);
+		set_dirty();
+		return true;
+
+
+	case TouchEvent::Type::End:
+		set_highlighted(false);
+		set_dirty();
+		if( on_select ) {
+			on_select();
+		}
+		return true;
+
+	default:
+		return false;
+	}
+}
+
 /* Image *****************************************************************/
 
 Image::Image(
diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp
index db3bdda43e6d06f3be59ea3a9b37a8ef2f5e1cdd..e4b8533bfec607deff8f0bf3787be9f4807d68de 100644
--- a/firmware/common/ui_widget.hpp
+++ b/firmware/common/ui_widget.hpp
@@ -394,6 +394,40 @@ private:
 	std::string text_;
 };
 
+class NewButton : public Widget {
+public:
+	std::function<void(void)> on_select { };
+	//std::function<void(NewButton&)> on_select { };
+	std::function<bool(NewButton&, KeyEvent)> on_dir { };
+	std::function<void(NewButton&)> on_highlight { };
+
+	NewButton(const NewButton&) = delete;
+	NewButton& operator=(const NewButton&) = delete;
+	NewButton(Rect parent_rect, std::string text, const Bitmap* bitmap);
+	NewButton(
+	) : NewButton { { }, { }, { } }
+	{
+	}
+
+	void set_bitmap(const Bitmap* bitmap);
+	void set_text(const std::string value);
+	void set_color(Color value);
+	std::string text() const;
+	const Bitmap* bitmap();
+	ui::Color color();
+
+	void on_focus() override;
+	bool on_key(const KeyEvent key) override;
+	bool on_touch(const TouchEvent event) override;
+
+	void paint(Painter& painter) override;
+
+private:
+	std::string text_;
+	Color color_ = Color::dark_cyan();
+	const Bitmap* bitmap_;
+};
+
 class Image : public Widget {
 public:
 	Image();
diff --git a/firmware/tools/strip_dfu.py b/firmware/tools/strip_dfu.py
deleted file mode 100755
index d3a9d8ebd6bd801466daa02255f6f9d6f2e8334a..0000000000000000000000000000000000000000
--- a/firmware/tools/strip_dfu.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python
-
-#
-# Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
-#
-# This file is part of PortaPack.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-import sys
-
-usage_message = """
-PortaPack DFU header stripper
-
-Usage: <command> <dfu_file> <output_file>
-"""
-
-def read_image(path):
-	f = open(path, 'rb')
-	data = f.read()
-	f.close()
-	return data
-
-def read_image_from_dfu(path):
-	data = read_image(path)
-	# Strip DFU header from file to get binary image.
-	return data[16:]
-
-def write_image(data, path):
-	f = open(path, 'wb')
-	f.write(data)
-	f.close()
-
-if len(sys.argv) != 3:
-	print(usage_message)
-	sys.exit(-1)
-
-dfu_image = read_image_from_dfu(sys.argv[1])
-output_path = sys.argv[2]
-
-write_image(dfu_image, output_path)
diff --git a/hackrf b/hackrf
new file mode 160000
index 0000000000000000000000000000000000000000..923f9fe617c9dcf00c97c18f5aa79e51720b9e59
--- /dev/null
+++ b/hackrf
@@ -0,0 +1 @@
+Subproject commit 923f9fe617c9dcf00c97c18f5aa79e51720b9e59
diff --git a/sdcard/FREQMAN/SCANNER.TXT b/sdcard/FREQMAN/SCANNER.TXT
new file mode 100644
index 0000000000000000000000000000000000000000..f34f29368a5cdeabfb2e5fb95c240f39a8dd1145
--- /dev/null
+++ b/sdcard/FREQMAN/SCANNER.TXT
@@ -0,0 +1,50 @@
+f=118000000
+f=119100000
+f=121100000
+f=122300000
+f=122400000
+f=131900000
+f=133250000
+f=144330000
+f=145025000
+f=145310000
+f=145375000
+f=145625000
+f=145700000
+f=145725000
+f=145750000
+f=146395000
+f=152250000
+f=154510000
+f=155210000
+f=156295000
+f=156700000
+f=158410000
+f=161550000
+f=161650000
+f=161700000
+f=163812500
+f=170900000
+f=172437500
+f=172725000
+f=173012500
+f=173325000
+f=173350000
+f=430625000
+f=430650000
+f=430725000
+f=430762500
+f=431200000
+f=431200000
+f=431500000
+f=431600000
+f=431825000
+f=438225000
+f=438250000
+f=438325000
+f=438362500
+f=438800000
+f=438800000
+f=439100000
+f=439200000
+f=439425000
diff --git a/tools/deploy-nightly.sh b/tools/deploy-nightly.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d0c1672a10eba3d61d5ff355e6ad0edf2b99eb5e
--- /dev/null
+++ b/tools/deploy-nightly.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+PUBLICATION_BRANCH=master
+# set -x
+cd $HOME
+# Checkout the branch
+git clone --branch=$PUBLICATION_BRANCH https://${GITHUB_TOKEN}@github.com/${ARTEFACT_REPO}.git publish
+cd publish
+# Update pages
+BUILD_PATH=$BUILD_DATE-$SHORT_COMMIT_HASH
+mkdir $BUILD_PATH
+cp $ARTEFACT_BASE/$BUILD_NAME.tar.xz $BUILD_PATH/
+cp $ARTEFACT_BASE/MD5SUMS $BUILD_PATH/
+cp $ARTEFACT_BASE/SHA256SUMS $BUILD_PATH/
+# Write index page
+cd $TRAVIS_BUILD_DIR
+COMMITS=`git log --oneline | awk '{print $1}'`
+cd $HOME/publish
+echo "
+<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">
+<html><head>
+	<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">
+	<title>$PROJECT_NAME Builds</title>
+</head>
+<body>
+<h2>$PROJECT_NAME Builds</h2>
+<table>
+" > index.html
+
+for commit in $COMMITS; do
+	FILEPATH=`find . -maxdepth 2 -name "*-$commit.tar.xz"`
+	if [ "$FILEPATH" != "" ]; then
+		FILEDIR=`dirname "${FILEPATH}"`
+		FILENAME=`basename "${FILEPATH}"`
+		FILEPATH=${FILEPATH:2}
+		# pushd "${FILEDIR}" 
+		# HASH_MD5=`md5sum --binary ${FILENAME}`
+		# HASH_SHA256=`sha256sum --binary ${FILENAME}`
+		# popd
+		echo "<tr><td><a href=\"$FILEPATH\">$FILENAME</a></td><td><a href=\"$FILEDIR/MD5SUMS\">MD5SUMS</a></td><td><a href=\"$FILEDIR/SHA256SUMS\">SHA256SUMS</a></td></tr>" >> index.html
+	fi
+	
+done
+
+echo "
+</table>
+</body></html>
+" >> index.html
+
+# Commit and push latest version
+git add $BUILD_PATH/$BUILD_NAME.tar.xz $BUILD_PATH/MD5SUMS $BUILD_PATH/SHA256SUMS index.html
+git config user.name  "Travis"
+git config user.email "travis@travis-ci.org"
+git commit -m "Build products for $SHORT_COMMIT_HASH, built on $TRAVIS_OS_NAME, log: $TRAVIS_BUILD_WEB_URL"
+if [ "$?" != "0" ]; then
+	echo "Looks like the commit failed"
+fi
+git push -fq origin $PUBLICATION_BRANCH
\ No newline at end of file