diff --git a/.vscode/launch.json b/.vscode/launch.json
index 09516bde169f299a9bf8dff4dcbeea85cadb641e..b26af04dec32a5e7c312920170ec4d623c32b343 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -155,6 +155,37 @@
             "svdFile": "STM32F413.svd",
             "cwd": "${workspaceRoot}"
         },
+        {
+            "type": "cortex-debug",
+            "request": "launch",
+            "servertype": "openocd",
+            "name": "serial (debug)",
+            "preLaunchTask": "cargo build --example serial --features stm32f4",
+            "executable": "./target/thumbv7em-none-eabihf/debug/examples/serial",
+            // uses local config files
+            "configFiles": [
+                "./stlink.cfg",
+                "./stm32f4x.cfg"
+            ],
+            "postLaunchCommands": [
+                "monitor arm semihosting enable"
+            ],
+            "swoConfig": {
+                "enabled": true,
+                "cpuFrequency": 16000000,
+                "swoFrequency": 2000000,
+                "source": "probe",
+                "decoders": [
+                    {
+                        "type": "console",
+                        "label": "ITM",
+                        "port": 0
+                    }
+                ]
+            },
+            "svdFile": "STM32F413.svd",
+            "cwd": "${workspaceRoot}"
+        },
         {
             "type": "cortex-debug",
             "request": "launch",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 841b6159e74eb564e792102a25249f1d34291753..39c537c2093ce7d20da4209bcb740b3192e3b986 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -195,5 +195,17 @@
                 "isDefault": true
             }
         },
+        {
+            "type": "shell",
+            "label": "cargo build --example serial --features stm32f4",
+            "command": "cargo build --example serial --features stm32f4",
+            "problemMatcher": [
+                "$rustc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
     ]
 }
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 418318fdb985abb7efae28a30e13559c5b59183d..210228ff90279434f3e6ee69d5ac64f29fc74853 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,9 +5,8 @@ readme = "README.md"
 name = "app"
 version = "0.1.0"
 
-[dependencies.cortex-m]
-version = "0.5.8"
-features = ["inline-asm"] # <- currently requires nightly compiler
+[patch.crates-io]
+stm32f4xx-hal = { path = "../stm32f4xx-hal" }
 
 [dependencies]
 cortex-m-rt = "0.6.7"
@@ -18,6 +17,11 @@ panic-halt = "0.2.0"
 panic-semihosting = "0.5.1"
 panic-itm = "0.4.0"
 bare-metal = "0.2.4"
+nb = "0.1.1"
+
+[dependencies.cortex-m]
+version = "0.5.8"
+features = ["inline-asm"] # <- currently requires nightly compiler
 
 # Uncomment for the allocator example.
 # alloc-cortex-m = "0.3.5"
@@ -25,11 +29,12 @@ bare-metal = "0.2.4"
 [dependencies.stm32f4]
 version = "0.5.0"
 features = ["stm32f411", "rt"]
-optional = true
+# optional = true
+
+[dependencies.stm32f4xx-hal]
+version = "0.2.8"
+features = ["stm32f411", "rt"]
 
-[[example]]
-name = "device"
-required-features = ["stm32f4"]
 
 # this lets you use `cargo fix`!
 [[bin]]
@@ -38,11 +43,12 @@ test = false
 bench = false
 
 [profile.dev]
-panic = "abort"
+incremental = false
+codegen-units = 1
 
 [profile.release]
 codegen-units = 1   # better optimizations
 debug = true        # symbols are nice and they don't increase the size on Flash
 lto = true          # better optimizations
-panic = "abort"
+
 
diff --git a/examples/serial.rs b/examples/serial.rs
new file mode 100644
index 0000000000000000000000000000000000000000..21833f2f162414811b84c22eb887760c6c45b101
--- /dev/null
+++ b/examples/serial.rs
@@ -0,0 +1,76 @@
+//! Serial interface loopback test
+//!
+//! You have to short the TX and RX pins to make this program work
+
+// #![deny(unsafe_code)]
+#![feature(uniform_paths)]
+// #![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate panic_halt;
+
+use cortex_m::asm;
+
+use nb::block;
+
+use cortex_m_rt::entry;
+// use stm32f4xx_hal::stm32f4::stm32f411 as device;
+
+extern crate stm32f4xx_hal as hal;
+// #[macro_use(block)]
+// extern crate nb;
+
+use crate::hal::prelude::*;
+use crate::hal::serial::{config::Config, Serial};
+//use crate::rt::ExceptionFrame;
+
+#[entry]
+fn main() -> ! {
+    let p = hal::stm32::Peripherals::take().unwrap();
+
+    let mut flash = p.FLASH.constrain();
+    let mut rcc = p.RCC.constrain();
+
+    let mut gpioa = p.GPIOA.split();
+    // let mut gpioa = p.GPIOA.split(&mut rcc.ahb1)
+
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    let tx = gpioa.pa2.into_alternate_af7();
+    let rx = gpioa.pa3.into_alternate_af7();
+
+    let serial = Serial::usart2(
+        p.USART2,
+        (tx, rx),
+        Config::default().baudrate(115_200.bps()),
+        clocks,
+    )
+    .unwrap();
+
+    // Separate out the sender and receiver of the serial port
+    let (mut tx, mut rx) = serial.split();
+
+    loop {
+        // Read character and echo it back
+        let received = block!(rx.read()).unwrap();
+        block!(tx.write(received)).ok();
+    }
+
+    // let (mut tx, mut rx) = serial.split();
+
+    // loop {
+    //     //ipln!("wait");
+    //     //spln!("wait");
+    //     if let Ok(byte) = block!(rx.read()) {
+    //         //ipln!("got {:?}", byte);
+    //         //spln!("got {:?}", byte);
+    //         block!(tx.write(byte)).ok();
+    //     } else {
+    //         //ipln!("buffer overflow");
+    //         //spln!("buffer overflow");
+    //     }
+    // }
+
+    //    loop {}
+}