diff --git a/.vscode/launch.json b/.vscode/launch.json
index 55e1c379068bc3406a0e108f894642887a75b091..31e7df83762744c2c32c5dc5ff824e1e5d8a4fda 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -15,8 +15,12 @@
             "remote": true,
             "autorun": [
                 "monitor reset init",
+                "monitor arm semihosting enable",
+                "monitor tpiu config internal /tmp/itm.log uart off 84000000", 
+                "monitor itm port 0 on",
                 "load",
-                "monitor reset init"
+                "monitor reset init",
+                "continue"
             ],
             "cwd": "${workspaceRoot}"
         },
diff --git a/Cargo.toml b/Cargo.toml
index 211507c5a7b8b254c8f6a9a9e1a6a67576f322c9..bd0ae4f2161c9e1ce87a78cebf6b3d215aaedcb6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,6 @@ repository = "https://github.com/Shawnshank/D7018E_Project"
 keywords = ["CoAP", "Embedded", "arm", "stm32"]
 license = "MIT OR Apache-2.0"
 
-
 [dependencies]
 cortex-m-semihosting = "0.2.0"
 static-ref = "0.2.0"
@@ -17,13 +16,13 @@ volatile-register = "0.2.0"
 m = "0.1.1"
 heapless = "0.2.4"
 f4 = {git = "https://github.com/jsjolund/f4"}
-#stm32f40x = "0.2.0"
 #xoap = { path = "../projects/xoap", version = "0.1.0", default-features = false, features = ["baremetal"] }# git = "https://github.com/Shawnshank/xoap"
 
-#[dependencies.stm32f4x_hal]
-#path = "stm32f4-hal/"
-#version = "0.1.1"
-#features = ["rt"]
+[dependencies.smoltcp]
+git = "https://github.com/m-labs/smoltcp"
+rev = "cd893e6a"
+default-features = false
+features = ["proto-ipv4", "socket-tcp"]
 
 [dependencies.stm32f40x]
 git = "https://gitlab.henriktjader.com/pln/STM32F40x.git"
diff --git a/src/esp.rs b/src/esp.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d01216d4e5b37f51feef3868166a7670ec91dcb0
--- /dev/null
+++ b/src/esp.rs
@@ -0,0 +1,87 @@
+use f4::Serial;
+use f4::serial::Usart;
+use core::any::Any;
+
+/// Timeout for the connection to ESP-Link
+pub const ESP_TIMEOUT: u32 = 2000;
+
+/// Codes used by slip
+#[derive(PartialEq, Eq)] // Fler
+pub enum SlipCodes {
+    SlipEnd = 0xC0,    // OCT: 0300
+    SlipEsc = 0xDB,    // OCT: 0333
+    SlipEscEnd = 0xDC, // OCT: 0334
+    SlipEscEsc = 0xDE, // OCT: 0335
+}
+
+/// Commands for the ESP-Link device
+#[derive(PartialEq, Eq)]
+pub enum Commands {
+    Null = 0,
+    Sync,
+    ResponseValue,
+    ResponseCallback,
+    WiFiStatus,
+    CallbackAdd,
+    CallbackEvents,
+    GetTime,
+    // MQTT
+    MQTTSetup = 10,
+    MQTTPublish,
+    MQTTSubscribe,
+    MQTTLWT,
+    // REST
+    RESTSetup = 20,
+    RESTRequest,
+    RESTSetHeader,
+    // Webserver
+    WEBSetup = 30,
+    WEBData,
+    // Socket connection
+    SOCKETSetup = 40,
+    SOCKETSend,
+}
+
+/// WiFi status given by ESP-Link
+#[derive(PartialEq, Eq)]
+pub enum WIFIStatus {
+    STATIONIdle = 0,
+    STATIONConnecting,
+    STATIONWrongPassword,
+    STATIONNoAPFound,
+    STATIONConnectFail,
+    STATIONGotIP,
+}
+
+/// Basic protocol to talk with the ESP-Link modul
+pub struct Protocol {
+    buffer: u8,
+    buffer_size: u16,
+    data_length: u16,
+    is_esc: u8, // bool instead?
+}
+
+struct Esp<T> 
+where T: Any + Usart,
+{
+    serial: Serial<'static, T>,
+    timeout: u32,
+}
+
+impl<T> Esp<T>
+where
+    T: Any + Usart,
+{
+    pub fn new(s: Serial<'static, T>, t: u32) -> Esp<T> {
+        Esp {
+            serial: s.clone(),
+            timeout: t,
+        }
+    }
+    pub fn sync(&self) {
+        let _serial = self.serial;
+        //serial.write(SlipCodes::SlipEnd); // send a END character to make sure the serial line is clear
+    }
+    pub fn process() {}
+    pub fn request(&self) {}
+}
diff --git a/src/ethernet.rs b/src/ethernet.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9ffc0f5cdf4eaaca17b37fc190b832671c0aa0c8
--- /dev/null
+++ b/src/ethernet.rs
@@ -0,0 +1,438 @@
+use core;
+
+use smoltcp::{self, phy::{self, DeviceCapabilities}, time::Instant, wire::EthernetAddress};
+
+const ETH_BUF_SIZE: usize = 1536;
+const ETH_NUM_TD: usize = 4;
+const ETH_NUM_RD: usize = 4;
+
+use ::main::ETH_PHY_ADDR;
+
+/// Transmit Descriptor representation
+///
+/// * tdes0: ownership bit and transmit settings
+/// * tdes1: transmit buffer lengths
+/// * tdes2: transmit buffer address
+/// * tdes3: not used
+///
+/// Note that Copy and Clone are derived to support initialising an array of TDes,
+/// but you may not move a TDes after its address has been given to the ETH_DMA engine.
+#[derive(Copy,Clone)]
+#[repr(C,packed)]
+struct TDes {
+    tdes0: u32,
+    tdes1: u32,
+    tdes2: u32,
+    tdes3: u32,
+}
+
+impl TDes {
+    /// Initialises this TDes to point at the given buffer.
+    pub fn init(&mut self, tdbuf: &[u32]) {
+        // Set FS and LS on each descriptor: each will hold a single full segment.
+        self.tdes0 = (1<<29) | (1<<28);
+        // Store pointer to associated buffer.
+        self.tdes2 = tdbuf.as_ptr() as u32;
+        // No second buffer.
+        self.tdes3 = 0;
+    }
+
+    /// Mark this TDes as end-of-ring.
+    pub fn set_end_of_ring(&mut self) {
+        self.tdes0 |= 1<<21;
+    }
+
+    /// Return true if the RDes is not currently owned by the DMA
+    pub fn available(&self) -> bool {
+        self.tdes0 & (1<<31) == 0
+    }
+
+    /// Release this RDes back to DMA engine for transmission
+    pub fn release(&mut self) {
+        self.tdes0 |= 1<<31;
+    }
+
+    /// Set the length of data in the buffer ponited to by this TDes
+    pub fn set_length(&mut self, length: usize) {
+        self.tdes1 = (length as u32) & 0x1FFF;
+    }
+
+    /// Access the buffer pointed to by this descriptor
+    pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] {
+        core::slice::from_raw_parts_mut(self.tdes2 as *mut _, self.tdes1 as usize & 0x1FFF)
+    }
+}
+
+/// Store a ring of TDes and associated buffers
+struct TDesRing {
+    td: [TDes; ETH_NUM_TD],
+    tbuf: [[u32; ETH_BUF_SIZE/4]; ETH_NUM_TD],
+    tdidx: usize,
+}
+
+static mut TDESRING: TDesRing = TDesRing {
+    td: [TDes { tdes0: 0, tdes1: 0, tdes2: 0, tdes3: 0 }; ETH_NUM_TD],
+    tbuf: [[0; ETH_BUF_SIZE/4]; ETH_NUM_TD],
+    tdidx: 0,
+};
+
+impl TDesRing {
+    /// Initialise this TDesRing
+    ///
+    /// The current memory address of the buffers inside this TDesRing will be stored in the
+    /// descriptors, so ensure the TDesRing is not moved after initialisation.
+    pub fn init(&mut self) {
+        for (td, tdbuf) in self.td.iter_mut().zip(self.tbuf.iter()) {
+            td.init(&tdbuf[..]);
+        }
+        self.td.last_mut().unwrap().set_end_of_ring();
+    }
+
+    /// Return the address of the start of the TDes ring
+    pub fn ptr(&self) -> *const TDes {
+        self.td.as_ptr()
+    }
+
+    /// Return true if a TDes is available for use
+    pub fn available(&self) -> bool {
+        self.td[self.tdidx].available()
+    }
+
+    /// Return the next available TDes if any are available, otherwise None
+    pub fn next(&mut self) -> Option<&mut TDes> {
+        if self.available() {
+            let rv = Some(&mut self.td[self.tdidx]);
+            self.tdidx = (self.tdidx + 1) % ETH_NUM_TD;
+            rv
+        } else {
+            None
+        }
+    }
+}
+
+/// Receive Descriptor representation
+///
+/// * rdes0: ownership bit and received packet metadata
+/// * rdes1: receive buffer lengths and settings
+/// * rdes2: receive buffer address
+/// * rdes3: not used
+///
+/// Note that Copy and Clone are derived to support initialising an array of TDes,
+/// but you may not move a TDes after its address has been given to the ETH_DMA engine.
+#[derive(Copy,Clone)]
+#[repr(C,packed)]
+struct RDes {
+    rdes0: u32,
+    rdes1: u32,
+    rdes2: u32,
+    rdes3: u32,
+}
+
+impl RDes {
+    /// Initialises this RDes to point at the given buffer.
+    pub fn init(&mut self, rdbuf: &[u32]) {
+        // Mark each RDes as owned by the DMA engine.
+        self.rdes0 = 1<<31;
+        // Store length of and pointer to associated buffer.
+        self.rdes1 = rdbuf.len() as u32 * 4;
+        self.rdes2 = rdbuf.as_ptr() as u32;
+        // No second buffer.
+        self.rdes3 = 0;
+    }
+
+    /// Mark this RDes as end-of-ring.
+    pub fn set_end_of_ring(&mut self) {
+        self.rdes1 |= 1<<15;
+    }
+
+    /// Return true if the RDes is not currently owned by the DMA
+    pub fn available(&self) -> bool {
+        self.rdes0 & (1<<31) == 0
+    }
+
+    /// Release this RDes back to the DMA engine
+    pub fn release(&mut self) {
+        self.rdes0 |= 1<<31;
+    }
+
+    /// Access the buffer pointed to by this descriptor
+    pub unsafe fn buf_as_slice(&self) -> &[u8] {
+        core::slice::from_raw_parts(self.rdes2 as *const _, (self.rdes0 >> 16) as usize & 0x3FFF)
+    }
+}
+
+/// Store a ring of RDes and associated buffers
+struct RDesRing {
+    rd: [RDes; ETH_NUM_RD],
+    rbuf: [[u32; ETH_BUF_SIZE/4]; ETH_NUM_RD],
+    rdidx: usize,
+}
+
+static mut RDESRING: RDesRing = RDesRing {
+    rd: [RDes { rdes0: 0, rdes1: 0, rdes2: 0, rdes3: 0 }; ETH_NUM_RD],
+    rbuf: [[0; ETH_BUF_SIZE/4]; ETH_NUM_RD],
+    rdidx: 0,
+};
+
+impl RDesRing {
+    /// Initialise this RDesRing
+    ///
+    /// The current memory address of the buffers inside this TDesRing will be stored in the
+    /// descriptors, so ensure the TDesRing is not moved after initialisation.
+    pub fn init(&mut self) {
+        for (rd, rdbuf) in self.rd.iter_mut().zip(self.rbuf.iter()) {
+            rd.init(&rdbuf[..]);
+        }
+        self.rd.last_mut().unwrap().set_end_of_ring();
+    }
+
+    /// Return the address of the start of the RDes ring
+    pub fn ptr(&self) -> *const RDes {
+        self.rd.as_ptr()
+    }
+
+    /// Return true if a RDes is available for use
+    pub fn available(&self) -> bool {
+        self.rd[self.rdidx].available()
+    }
+
+    /// Return the next available RDes if any are available, otherwise None
+    pub fn next(&mut self) -> Option<&mut RDes> {
+        if self.available() {
+            let rv = Some(&mut self.rd[self.rdidx]);
+            self.rdidx = (self.rdidx + 1) % ETH_NUM_RD;
+            rv
+        } else {
+            None
+        }
+    }
+}
+
+/// Ethernet device driver
+pub struct EthernetDevice {
+    rdring: &'static mut RDesRing,
+    tdring: &'static mut TDesRing,
+    eth_mac: stm32f407::ETHERNET_MAC,
+    eth_dma: stm32f407::ETHERNET_DMA,
+}
+
+impl EthernetDevice {
+    /// Create a new uninitialised EthernetDevice.
+    ///
+    /// You must move in ETH_MAC, ETH_DMA, and they are then kept by the device.
+    pub fn new(eth_mac: stm32f407::ETHERNET_MAC, eth_dma: stm32f407::ETHERNET_DMA)
+    -> EthernetDevice {
+        unsafe { EthernetDevice { rdring: &mut RDESRING, tdring: &mut TDESRING, eth_mac, eth_dma }}
+    }
+
+    /// Initialise the ethernet driver.
+    ///
+    /// Sets up the descriptor structures, sets up the peripheral clocks and GPIO configuration,
+    /// and configures the ETH MAC and DMA peripherals.
+    ///
+    /// Brings up the PHY and then blocks waiting for a network link.
+    pub fn init(&mut self, rcc: &mut stm32f407::RCC, addr: EthernetAddress) {
+        self.tdring.init();
+        self.rdring.init();
+
+        self.init_peripherals(rcc, addr);
+
+        self.phy_reset();
+        self.phy_init();
+    }
+
+    pub fn link_established(&mut self) -> bool {
+        return self.phy_poll_link()
+    }
+
+    pub fn block_until_link(&mut self) {
+        while !self.link_established() {}
+    }
+
+    /// Resume suspended TX DMA operation
+    pub fn resume_tx_dma(&mut self) {
+        if self.eth_dma.dmasr.read().tps().is_suspended() {
+            self.eth_dma.dmatpdr.write(|w| w.tpd().poll());
+        }
+    }
+
+    /// Resume suspended RX DMA operation
+    pub fn resume_rx_dma(&mut self) {
+        if self.eth_dma.dmasr.read().rps().is_suspended() {
+            self.eth_dma.dmarpdr.write(|w| w.rpd().poll());
+        }
+    }
+
+    /// Sets up the device peripherals.
+    fn init_peripherals(&mut self, rcc: &mut stm32f407::RCC, mac: EthernetAddress) {
+        // Reset ETH_MAC and ETH_DMA
+        rcc.ahb1rstr.modify(|_, w| w.ethmacrst().reset());
+        rcc.ahb1rstr.modify(|_, w| w.ethmacrst().clear_bit());
+        self.eth_dma.dmabmr.modify(|_, w| w.sr().reset());
+        while self.eth_dma.dmabmr.read().sr().is_reset() {}
+
+        // Set MAC address
+        let mac = mac.as_bytes();
+        self.eth_mac.maca0lr.write(|w| w.maca0l().bits(
+            (mac[0] as u32) << 0 | (mac[1] as u32) << 8 |
+            (mac[2] as u32) <<16 | (mac[3] as u32) <<24));
+        self.eth_mac.maca0hr.write(|w| w.maca0h().bits(
+            (mac[4] as u16) << 0 | (mac[5] as u16) << 8));
+
+        // Enable RX and TX. We'll set link speed and duplex at link-up.
+        self.eth_mac.maccr.write(|w|
+            w.re().enabled()
+             .te().enabled()
+             .cstf().enabled()
+        );
+
+        // Tell the ETH DMA the start of each ring
+        self.eth_dma.dmatdlar.write(|w| w.stl().bits(self.tdring.ptr() as u32));
+        self.eth_dma.dmardlar.write(|w| w.srl().bits(self.rdring.ptr() as u32));
+
+        // Set DMA bus mode
+        self.eth_dma.dmabmr.modify(|_, w|
+            w.aab().aligned()
+             .pbl().pbl1()
+        );
+
+        // Flush TX FIFO
+        self.eth_dma.dmaomr.write(|w| w.ftf().flush());
+        while self.eth_dma.dmaomr.read().ftf().is_flush() {}
+
+        // Set DMA operation mode to store-and-forward and start DMA
+        self.eth_dma.dmaomr.write(|w|
+            w.rsf().store_forward()
+             .tsf().store_forward()
+             .st().started()
+             .sr().started()
+        );
+    }
+
+    /// Read a register over SMI.
+    fn smi_read(&mut self, reg: u8) -> u16 {
+        // Use PHY address 00000, set register address, set clock to HCLK/102, start read.
+        self.eth_mac.macmiiar.write(|w|
+            w.mb().busy()
+             .pa().bits(ETH_PHY_ADDR)
+             .cr().cr_150_168()
+             .mr().bits(reg)
+        );
+
+        // Wait for read
+        while self.eth_mac.macmiiar.read().mb().is_busy() {}
+
+        // Return result
+        self.eth_mac.macmiidr.read().td().bits()
+    }
+
+    /// Write a register over SMI.
+    fn smi_write(&mut self, reg: u8, val: u16) {
+        // Use PHY address 00000, set write data, set register address, set clock to HCLK/102,
+        // start write operation.
+        self.eth_mac.macmiidr.write(|w| w.td().bits(val));
+        self.eth_mac.macmiiar.write(|w|
+            w.mb().busy()
+             .pa().bits(ETH_PHY_ADDR)
+             .mw().write()
+             .cr().cr_150_168()
+             .mr().bits(reg)
+        );
+
+        while self.eth_mac.macmiiar.read().mb().is_busy() {}
+    }
+
+    /// Reset the connected PHY and wait for it to come out of reset.
+    fn phy_reset(&mut self) {
+        self.smi_write(0x00, 1<<15);
+        while self.smi_read(0x00) & (1<<15) == (1<<15) {}
+    }
+
+    /// Command connected PHY to initialise.
+    fn phy_init(&mut self) {
+        self.smi_write(0x00, 1<<12);
+    }
+
+    /// Poll PHY to determine link status.
+    fn phy_poll_link(&mut self) -> bool {
+        let bsr = self.smi_read(0x01);
+        let bcr = self.smi_read(0x00);
+        let lpa = self.smi_read(0x05);
+
+        // No link without autonegotiate
+        if bcr & (1<<12) == 0 { return false; }
+        // No link if link is down
+        if bsr & (1<< 2) == 0 { return false; }
+        // No link if remote fault
+        if bsr & (1<< 4) != 0 { return false; }
+        // No link if autonegotiate incomplete
+        if bsr & (1<< 5) == 0 { return false; }
+        // No link if other side can't do 100Mbps full duplex
+        if lpa & (1<< 8) == 0 { return false; }
+
+        // Got link. Configure MAC to 100Mbit/s and full duplex.
+        self.eth_mac.maccr.modify(|_, w|
+            w.fes().fes100()
+             .dm().full_duplex()
+        );
+
+        true
+    }
+}
+
+pub struct TxToken(*mut EthernetDevice);
+pub struct RxToken(*mut EthernetDevice);
+
+impl phy::TxToken for TxToken {
+    fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
+        where F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
+    {
+        let tdes = unsafe { (*self.0).tdring.next().unwrap() };
+        tdes.set_length(len);
+        let result = f(unsafe { tdes.buf_as_slice_mut() });
+        tdes.release();
+        unsafe { (*self.0).resume_tx_dma() };
+        result
+    }
+}
+
+impl phy::RxToken for RxToken {
+    fn consume<R, F>(self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
+        where F: FnOnce(&[u8]) -> smoltcp::Result<R>
+    {
+        let rdes = unsafe { (*self.0).rdring.next().unwrap() };
+        let result = f(unsafe { rdes.buf_as_slice() });
+        rdes.release();
+        unsafe { (*self.0).resume_rx_dma() };
+        result
+    }
+}
+
+// Implement the smoltcp Device interface
+impl<'a> phy::Device<'a> for EthernetDevice {
+    type RxToken = RxToken;
+    type TxToken = TxToken;
+
+    fn capabilities(&self) -> DeviceCapabilities {
+        let mut caps = DeviceCapabilities::default();
+        caps.max_transmission_unit = 1500;
+        caps.max_burst_size = Some(core::cmp::min(ETH_NUM_TD, ETH_NUM_RD));
+        caps
+    }
+
+    fn receive(&mut self) -> Option<(RxToken, TxToken)> {
+        if self.rdring.available() && self.tdring.available() {
+            Some((RxToken(self), TxToken(self)))
+        } else {
+            None
+        }
+    }
+
+    fn transmit(&mut self) -> Option<TxToken> {
+        if self.tdring.available() {
+            Some(TxToken(self))
+        } else {
+            None
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 7fbf1ce8cfd72b297495596053220e4d3a5f109e..1aa86f2c4ceed1599fdb0eed7af250edd3a9df47 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,19 +9,39 @@ extern crate cortex_m_debug;
 extern crate cortex_m_rtfm as rtfm;
 extern crate f4;
 extern crate heapless;
+//extern crate smoltcp;
 extern crate stm32f40x;
 
+// CORE
 use core::result::Result;
+//use core::str;
+// RTFM
+use rtfm::{app, Resource, Threshold};
+// F4
 use f4::prelude::*;
-use f4::{Serial, Timer, I2c, clock};
+use f4::{clock, I2c, Serial, Timer};
 use f4::serial::Event;
 use f4::time::{Hertz, Seconds};
 use stm32f40x::I2C1;
-use rtfm::{app, Resource, Threshold};
+// SMOLTCP
+/*
+use smoltcp::phy::Loopback;
+use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
+use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache};
+use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
+use smoltcp::time::{Duration, Instant};
+*/
+// SLIP
+// mod slip;
+// ESP-LINK
+mod esp;
 
 // CONFIGURATION
+pub const TCP_PORT: u16 = 7777;
+pub const ETH_PHY_ADDR: u8 = 1;
+//const MAX_UDP_PACKET_SIZE: usize = 2048;
 const BAUD_RATE: Hertz = Hertz(115_200);
-const RX_BUFFER_SIZE: usize = core::mem::size_of::<u32>();
+const I2C_BUFFER_SIZE: usize = core::mem::size_of::<u32>();
 const SENSOR_READ_TIME: Seconds = Seconds(60); // corresponds to ~20 sec for some reson. Maybe something with the increased clockspeed.
 
 #[derive(Debug)]
@@ -31,29 +51,39 @@ pub enum Error {
     Unknown,
 }
 
+
+
 app! {
     device: f4::stm32f40x,
 
     resources: {
         static TEMPERATURE: f32 = 0.0;
         static HUMIDITY: f32 = 0.0;
+        //static PACKET: Vec<u8, [u8; MAX_UDP_PACKET_SIZE]> = Vec::new();
     },
 
     tasks: {
         USART2: {
             path: rx,
-            priority: 1,
+            priority: 2,
             resources: [USART2, TEMPERATURE, HUMIDITY],
         },
+
         EXTI1: {
             path: read_sensor,
-            priority: 3,
+            priority: 4,
             resources: [I2C1, TEMPERATURE, HUMIDITY],
         },
-
+        /*
+        EXTI2: {
+            path: udp_server,
+            priority: 1,
+            resources: [PACKET],
+        },
+        */
         TIM2: {
             path: sensor_update,
-            priority: 2,
+            priority: 3,
             resources: [TIM2],
         },
     },
@@ -67,6 +97,8 @@ fn init(p: init::Peripherals, _r: init::Resources) {
     let serial = Serial(p.USART2);
     serial.init(BAUD_RATE.invert(), None, p.GPIOA, p.RCC);
     serial.listen(Event::Rxne);
+    // Listen to serial input on the receive DMA
+    //serial.read_exact(p.DMA1, r.RX_BUFFER).unwrap();
 
     // Start the I2C peripheral
     let i2c = I2c(p.I2C1);
@@ -79,59 +111,76 @@ fn init(p: init::Peripherals, _r: init::Resources) {
     timer.resume();
 }
 
+/// Recives the data on the serial interface
 fn rx(_t: &mut Threshold, r: USART2::Resources) {
     // Serial interface
     let serial = Serial(&**r.USART2);
     // Match to the byte read
-    match serial.read() {
-        Ok(_) => {
-            // DEBUG
-            let mut _temp: f32 = 0.0;
-            let mut _rh: f32 = 0.0;
-            r.TEMPERATURE.claim(_t, |temp, _| {
-                _temp = **temp;
-            });
-            r.HUMIDITY.claim(_t, |rh, _| {
-                _rh = **rh;
-            });
-
-            sprintln!("Temp: {}", _temp);
-            sprintln!("RH: {}", _rh);
-            //while serial.write(byte)
+    let msg = serial.read();
+    match msg {
+        Ok(byte) => {
+            match byte {
+                b'd' => {
+                    // DEBUG
+                    let mut _temp: f32 = 0.0;
+                    let mut _rh: f32 = 0.0;
+                    r.TEMPERATURE.claim(_t, |temp, _| {
+                        _temp = **temp;
+                    });
+                    r.HUMIDITY.claim(_t, |rh, _| {
+                        _rh = **rh;
+                    });
+
+                    sprintln!("Temp: {}", _temp);
+                    sprintln!("RH: {}", _rh);
+
+                    //let test: u8 = _temp as u8;
+
+                    //serial.write(test).unwrap();
+                }
+                _ => serial.write(byte).unwrap(),
+            }
         }
         Err(_) => {
             r.USART2.dr.read(); // clear the error by reading the data register
         }
     }
+    rtfm::set_pending(f4::stm32f40x::Interrupt::EXTI2);
+}
+
+/*
+fn udp_server(_t: &mut Threshold _r: EXTI2::Resources) {
+
 }
+*/
 
-// Interrupt for the timer for the update of the Temp/RH sensor.
+/// Interrupt for the timer for the update of the Temp/RH sensor.
 fn sensor_update(_t: &mut Threshold, r: TIM2::Resources) {
     r.TIM2.sr.modify(|_, w| w.uif().clear_bit());
     // Sets a new sensor read as a pending task
     rtfm::set_pending(f4::stm32f40x::Interrupt::EXTI1);
 }
 
-// Reads and interpretes the sensor data.
+/// Reads and interpretes the sensor data.
 fn read_sensor(_t: &mut Threshold, r: EXTI1::Resources) {
     // I2C interface.
     let i2c = I2c(&**r.I2C1);
     // 4 Byte buffer to store the sensor data in.
-    let mut rx: [u8; RX_BUFFER_SIZE] = [0; RX_BUFFER_SIZE];
+    let mut rx: [u8; I2C_BUFFER_SIZE] = [0; I2C_BUFFER_SIZE];
     match get_data(&i2c, &mut rx) {
         Err(err) => match err {
             Error::StaleData => {
-                sprintln!("ERROR: Sensor has Stale data");
+                sprintln!("SENSOR ERROR: Sensor has Stale data");
             }
             Error::CommandMode => {
-                sprintln!("ERROR: Sensor in command mode");
+                sprintln!("SENSOR ERROR: Sensor in command mode");
             }
             Error::Unknown => {
-                sprintln!("ERROR: Sensor displayed unknown error");
+                sprintln!("SENSOR ERROR: Sensor displayed unknown error");
             }
         },
         Ok(_) => {
-            // Overly explicit but the calculation gets sad otherwise.
+            // Overly explicit but the calculation gets mad otherwise.
             let mut lt: u16 = rx[3] as u16;
             let mut mt: u16 = rx[2] as u16;
             let mut lh: u16 = rx[1] as u16;
@@ -160,18 +209,18 @@ fn read_sensor(_t: &mut Threshold, r: EXTI1::Resources) {
     }
 }
 
-// Gets the data from the Temp/RH sensor over the I2C bus
-fn get_data(i2c: &I2c<I2C1>, rx_buffer: &mut [u8; RX_BUFFER_SIZE]) -> Result<(), Error> {
+/// Gets the data from the Temp/RH sensor over the I2C bus
+fn get_data(i2c: &I2c<I2C1>, rx_buffer: &mut [u8; I2C_BUFFER_SIZE]) -> Result<(), Error> {
     // Send a Measurment Request (MR) to the sensor
     while i2c.start(0x4E).is_err() {}
     while i2c.stop().is_err() {}
-    sprintln!("READ"); // TODO: NEEDS TO BE SLOWED DOWN, HERE WITH THE USE OF SEMIHOSTING
+    sprintln!("SENSOR READ"); // TODO: NEEDS TO BE SLOWED DOWN, HERE WITH THE USE OF SEMIHOSTING
 
     // Read incoming bytes and ACK them or nack if it is the last byte.
     while i2c.start(0x4F).is_err() {}
-    for i in 0..RX_BUFFER_SIZE {
+    for i in 0..I2C_BUFFER_SIZE {
         rx_buffer[i] = loop {
-            if i == RX_BUFFER_SIZE - 1 {
+            if i == I2C_BUFFER_SIZE - 1 {
                 // Do not ACK the last byte received and send STOP
                 if let Ok(byte) = i2c.read_nack() {
                     break byte;
diff --git a/src/network.rs b/src/network.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c1eea217c8f64decbe28c0299da9f92e8c98dc72
--- /dev/null
+++ b/src/network.rs
@@ -0,0 +1,187 @@
+use core::fmt::Write;
+
+use smoltcp;
+use smoltcp::time::Instant;
+use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
+use smoltcp::iface::{Neighbor, NeighborCache, EthernetInterface, EthernetInterfaceBuilder};
+use smoltcp::socket::{SocketSet, SocketSetItem, SocketHandle, TcpSocket, TcpSocketBuffer};
+
+use byteorder::{ByteOrder, LittleEndian};
+
+use ::flash;
+use ::build_info;
+use ::Error;
+use ethernet::EthernetDevice;
+
+const CMD_INFO: u32 = 0;
+const CMD_READ: u32 = 1;
+const CMD_ERASE: u32 = 2;
+const CMD_WRITE: u32 = 3;
+const CMD_BOOT: u32 = 4;
+
+use ::config::TCP_PORT;
+
+/// Read an address and length from the socket
+fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) {
+    let mut adr = [0u8; 4];
+    let mut len = [0u8; 4];
+    socket.recv_slice(&mut adr[..]).ok();
+    socket.recv_slice(&mut len[..]).ok();
+    let adr = LittleEndian::read_u32(&adr);
+    let len = LittleEndian::read_u32(&len);
+    (adr, len as usize)
+}
+
+/// Send a status word back at the start of a response
+fn send_status(socket: &mut TcpSocket, status: ::Error) {
+    let mut resp = [0u8; 4];
+    LittleEndian::write_u32(&mut resp, status as u32);
+    socket.send_slice(&resp).unwrap();
+}
+
+/// Respond to the information request command with our build information.
+fn cmd_info(socket: &mut TcpSocket) {
+
+    // Read the device unique ID
+    let id1: u32 = unsafe { *(0x1FFF_7A10 as *const u32) };
+    let id2: u32 = unsafe { *(0x1FFF_7A14 as *const u32) };
+    let id3: u32 = unsafe { *(0x1FFF_7A18 as *const u32) };
+
+    send_status(socket, Error::Success);
+    write!(socket, "blethrs {} {}\r\nBuilt: {}\r\nCompiler: {}\r\nMCU ID: {:08X}{:08X}{:08X}\r\n",
+           build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap(), build_info::BUILT_TIME_UTC,
+           build_info::RUSTC_VERSION, id1, id2, id3).ok();
+}
+
+fn cmd_read(socket: &mut TcpSocket) {
+    let (adr, len) = read_adr_len(socket);
+    match flash::read(adr, len) {
+        Ok(data) => {
+            send_status(socket, Error::Success);
+            socket.send_slice(data).unwrap();
+        },
+        Err(err) => send_status(socket, err),
+    };
+}
+
+fn cmd_erase(socket: &mut TcpSocket) {
+    let (adr, len) = read_adr_len(socket);
+    match flash::erase(adr, len) {
+        Ok(()) => send_status(socket, Error::Success),
+        Err(err) => send_status(socket, err),
+    }
+}
+
+fn cmd_write(socket: &mut TcpSocket) {
+    let (adr, len) = read_adr_len(socket);
+    match socket.recv(|buf| (buf.len(), flash::write(adr, len, buf))) {
+        Ok(Ok(())) => send_status(socket, Error::Success),
+        Ok(Err(err)) => send_status(socket, err),
+        Err(_) => send_status(socket, Error::NetworkError),
+    }
+}
+
+fn cmd_boot(socket: &mut TcpSocket) {
+    send_status(socket, Error::Success);
+    ::schedule_reset(50);
+}
+
+// Stores the underlying data buffers. If these were included in Network,
+// they couldn't live in BSS and therefore take up a load of flash space.
+struct NetworkBuffers {
+    tcp_tx_buf: [u8; 1536],
+    tcp_rx_buf: [u8; 1536],
+}
+
+static mut NETWORK_BUFFERS: NetworkBuffers = NetworkBuffers {
+    tcp_tx_buf: [0u8; 1536],
+    tcp_rx_buf: [0u8; 1536],
+};
+
+// Stores all the smoltcp required structs.
+pub struct Network<'a> {
+    neighbor_cache_storage: [Option<(IpAddress, Neighbor)>; 16],
+    ip_addr: Option<[IpCidr; 1]>,
+    eth_iface: Option<EthernetInterface<'a, 'a, EthernetDevice>>,
+    sockets_storage: [Option<SocketSetItem<'a, 'a>>; 1],
+    sockets: Option<SocketSet<'a, 'a, 'a>>,
+    tcp_handle: Option<SocketHandle>,
+    initialised: bool,
+}
+
+pub static mut NETWORK: Network = Network {
+    neighbor_cache_storage: [None; 16],
+    ip_addr: None,
+    eth_iface: None,
+    sockets_storage: [None],
+    sockets: None,
+    tcp_handle: None,
+    initialised: false,
+};
+
+/// Initialise the static NETWORK.
+///
+/// Sets up the required EthernetInterface and sockets.
+pub unsafe fn init<'a>(eth_dev: EthernetDevice, mac_addr: EthernetAddress, ip_addr: IpCidr) {
+    let neighbor_cache = NeighborCache::new(&mut NETWORK.neighbor_cache_storage.as_mut()[..]);
+
+    NETWORK.ip_addr = Some([ip_addr]);
+    NETWORK.eth_iface = Some(EthernetInterfaceBuilder::new(eth_dev)
+                            .ethernet_addr(mac_addr)
+                            .neighbor_cache(neighbor_cache)
+                            .ip_addrs(&mut NETWORK.ip_addr.as_mut().unwrap()[..])
+                            .finalize());
+
+    NETWORK.sockets = Some(SocketSet::new(&mut NETWORK.sockets_storage.as_mut()[..]));
+    let tcp_rx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_rx_buf.as_mut()[..]);
+    let tcp_tx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_tx_buf.as_mut()[..]);
+    let tcp_socket = TcpSocket::new(tcp_rx_buf, tcp_tx_buf);
+    NETWORK.tcp_handle = Some(NETWORK.sockets.as_mut().unwrap().add(tcp_socket));
+    NETWORK.initialised = true;
+}
+
+/// Poll network stack.
+///
+/// Arrange for this function to be called frequently.
+pub fn poll(time_ms: i64) {
+    unsafe {
+        // Bail out early if NETWORK is not initialised.
+        if !NETWORK.initialised {
+            return;
+        }
+
+        let sockets = NETWORK.sockets.as_mut().unwrap();
+
+        // Handle TCP
+        {
+            let mut socket = sockets.get::<TcpSocket>(NETWORK.tcp_handle.unwrap());
+            if !socket.is_open() {
+                socket.listen(TCP_PORT).unwrap();
+            }
+            if !socket.may_recv() && socket.may_send() {
+                socket.close();
+            }
+            if socket.can_recv() {
+                let mut cmd = [0u8; 4];
+                socket.recv_slice(&mut cmd[..]).ok();
+                let cmd = LittleEndian::read_u32(&cmd[..]);
+                match cmd {
+                   CMD_INFO  => cmd_info(&mut socket),
+                   CMD_READ => cmd_read(&mut socket),
+                   CMD_ERASE => cmd_erase(&mut socket),
+                   CMD_WRITE => cmd_write(&mut socket),
+                   CMD_BOOT => cmd_boot(&mut socket),
+                    _ => (),
+                };
+                socket.close();
+            }
+        }
+
+        // Poll smoltcp
+        let timestamp = Instant::from_millis(time_ms);
+        match NETWORK.eth_iface.as_mut().unwrap().poll(sockets, timestamp) {
+            Ok(_) | Err(smoltcp::Error::Exhausted) => (),
+            Err(_) => (),
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/slip.rs b/src/slip.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0ee4bbaa73a6d61df83be657d731f94829327b8e
--- /dev/null
+++ b/src/slip.rs
@@ -0,0 +1,43 @@
+#![allow(dead_code)]
+
+use heapless::RingBuffer;
+use f4::Serial;
+
+/// Codes used by slip
+#[derive(PartialEq)] // Fler
+pub enum SlipCodes {
+    SlipEnd = 0xC0,    // OCT: 0300
+    SlipEsc = 0xDB,    // OCT: 0333
+    SlipEscEnd = 0xDC, // OCT: 0334
+    SlipEscEsc = 0xDE, // OCT: 0335
+}
+
+pub struct SlipBuffer {
+    buffer: RingBuffer<u8, [u8; 4]>,
+    last: SlipCodes,
+    packet_count: u8,
+}
+pub impl SlipBuffer {
+    pub fn new() -> Self {
+        SlipBuffer {
+            buffer: RingBuffer::new(),
+            last: SlipCodes::SlipEnd,
+            packet_count: 0,
+        }
+    }
+}
+
+pub struct SlipPacket {
+    buffer: SlipBuffer,
+}
+
+pub struct Slip {
+    todo: u8,
+}
+
+pub impl Slip {
+    pub fn send_packet() {}
+    pub fn read_packet(&mut packet: SlipPacket) -> SlipPacket {
+
+    }
+}