diff --git a/src/controller.rs b/src/controller.rs
index ac79f9fba243a96c65ae191831f555392697ab72..aadc0a8828cefb21094f866fb4f7d41f9edd6078 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -65,11 +65,16 @@ impl View {
     pub fn from_contact_options_id(
         id: usize,
         contact_id: usize,
-        mut contacts: ContactIter,
+        storage: &Storage,
     ) -> Option<Self> {
+        let mut contacts = storage.contacts();
+
         match id {
             0 => {
-                let contact = contacts.nth(contact_id).unwrap_or(Contact::empty()).clone();
+                let contact = contacts
+                    .nth(contact_id)
+                    .unwrap_or_else(|| storage.new_contact())
+                    .clone();
                 Some(View::ContactView(
                     ContactView::new(contact, false),
                     contact_id,
@@ -77,7 +82,10 @@ impl View {
             }
 
             1 => {
-                let contact = contacts.nth(contact_id).unwrap_or(Contact::empty()).clone();
+                let contact = contacts
+                    .nth(contact_id)
+                    .unwrap_or_else(|| storage.new_contact())
+                    .clone();
                 Some(View::ContactView(
                     ContactView::new(contact, true),
                     contact_id,
@@ -183,24 +191,18 @@ impl Element for Controller {
 
             (View::Contacts(c), KeyCode::Touchpad) => {
                 if c.selected() == self.storage.contacts_len() {
-                    // TODO flash
-                    // self.contacts.contacts.push(Contact::default()).unwrap();
-
                     // Go direct to edit page
                     self.cur_view =
-                        View::from_contact_options_id(1, c.selected(), self.storage.contacts())
-                            .unwrap();
+                        View::from_contact_options_id(1, c.selected(), &self.storage).unwrap();
                 } else {
                     self.cur_view = View::new_contact_options(c.selected());
                 }
             }
 
             (View::ContactOptions(c, contact_id), KeyCode::Touchpad) => {
-                if let Some(new_view) = View::from_contact_options_id(
-                    c.selected(),
-                    *contact_id,
-                    self.storage.contacts(),
-                ) {
+                if let Some(new_view) =
+                    View::from_contact_options_id(c.selected(), *contact_id, &self.storage)
+                {
                     self.cur_view = new_view;
                 }
             }
@@ -216,8 +218,8 @@ impl Element for Controller {
             }
 
             (View::ContactView(cv, contact_id), KeyCode::Back) => {
-                // TODO save contact
-                // self.storage.contacts().contacts[*contact_id] = cv.contact();
+                self.storage.save_contact(cv.contact());
+
                 self.cur_view = View::new_contact_options(*contact_id)
             }
 
diff --git a/src/gui/contact_view.rs b/src/gui/contact_view.rs
index 09429decb9a1014b6745a03a1dc0320dda4335f3..2c721abf9b9f1ccee66858545abda11e21eb5ca6 100644
--- a/src/gui/contact_view.rs
+++ b/src/gui/contact_view.rs
@@ -77,24 +77,22 @@ impl ContactView {
         }
     }
 
-    pub fn contact(&self) -> Contact {
+    pub fn contact(&mut self) -> Contact {
         if self.editable {
-            Contact {
-                callsign: self
-                    .callsign
-                    .get_text()
-                    .try_into()
-                    .unwrap_or_else(|_| self.contact.callsign.clone()),
-                ssid: self.ssid.get_text().parse().unwrap_or(self.contact.ssid),
-                name: self
-                    .name
-                    .get_text()
-                    .try_into()
-                    .unwrap_or_else(|_| self.contact.name.clone()),
+            if let Ok(callsign) = self.callsign.get_text().try_into() {
+                self.contact.callsign = callsign;
+            }
+
+            if let Ok(ssid) = self.ssid.get_text().parse() {
+                self.contact.ssid = ssid;
+            }
+
+            if let Ok(name) = self.name.get_text().try_into() {
+                self.contact.name = name;
             }
-        } else {
-            self.contact.clone()
         }
+
+        self.contact.clone()
     }
 }
 
diff --git a/src/storage.rs b/src/storage.rs
index c27ae3b538c3308e2f08631d18e5290cce7a85ba..ce12a39872b003388cbfd6fe0d59b7552d56125e 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -29,6 +29,19 @@ impl Storage {
         }
     }
 
+    pub fn save_contact(&mut self, contact: Contact) {
+        let len = self.contact_header.num_contacts;
+
+        // if id isn't less than the len, we're inserting a new contact
+        if contact.id >= usize::from(len) {
+            assert!(usize::from(len) < MAX_CONTACTS);
+            self.contact_header.num_contacts += 1;
+            self.contact_header.write(&self);
+        }
+
+        contact.write(&self);
+    }
+
     pub fn contacts(&self) -> ContactIter {
         ContactIter::new(self.contact_header.num_contacts.into())
     }
@@ -36,6 +49,22 @@ impl Storage {
     pub fn contacts_len(&self) -> usize {
         self.contact_header.num_contacts.into()
     }
+
+    // Note - calling this twice without saving one
+    // will cause a duplicate ID
+    // We don't increment the len until the contact is actually saved
+    pub fn new_contact(&self) -> Contact {
+        Contact {
+            callsign: String::new(),
+            ssid: 0,
+            name: String::new(),
+            id: usize::from(self.contact_header.num_contacts),
+        }
+    }
+
+    fn write_data(&self, start_addr: usize, buf: &[u8]) {
+        todo!()
+    }
 }
 
 // Up to 100 contacts
@@ -46,7 +75,7 @@ struct ContactHeader {
 
 impl ContactHeader {
     fn read() -> Self {
-        let bytes = unsafe { from_raw_parts(CONTACT_HEADER_START as *mut u8, CONTACT_LENGTH) };
+        let bytes = unsafe { from_raw_parts(CONTACT_HEADER_START as *mut u8, CONTACT_HEADER_SIZE) };
         let num_contacts = bytes[0];
         let checksum_expected = u16::from_le_bytes([bytes[1], bytes[2]]);
         // CRC checksum is very overkill right now
@@ -59,6 +88,15 @@ impl ContactHeader {
             Self { num_contacts: 0 }
         }
     }
+
+    fn write(&self, storage: &Storage) {
+        let mut buf = [0; CONTACT_HEADER_SIZE];
+        buf[0] = self.num_contacts;
+        let checksum = X25.checksum(&[self.num_contacts]);
+        buf[1..].copy_from_slice(&checksum.to_le_bytes());
+
+        storage.write_data(CONTACT_HEADER_START, &buf);
+    }
 }
 
 #[derive(Clone)]
@@ -66,6 +104,7 @@ pub struct Contact {
     pub callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>,
     pub ssid: u8,
     pub name: String<MAX_CONTACT_NAME_LENGTH>,
+    id: usize,
 }
 
 impl Contact {
@@ -101,15 +140,27 @@ impl Contact {
             callsign,
             ssid,
             name,
+            id: idx,
         })
     }
 
-    pub fn empty() -> Self {
-        Self {
-            callsign: String::new(),
-            ssid: 0,
-            name: String::new(),
-        }
+    fn write(&self, storage: &Storage) {
+        let start_addr = CONTACT_HEADER_START + CONTACT_HEADER_SIZE + self.id * CONTACT_LENGTH;
+
+        let mut buf = [0; CONTACT_LENGTH];
+        buf[0] = self.callsign.len().try_into().unwrap();
+        buf[1] = self.name.len().try_into().unwrap();
+        buf[2] = self.ssid;
+
+        let callsign_bytes = self.callsign.as_bytes();
+        let name_bytes = self.name.as_bytes();
+        buf[3..(3 + callsign_bytes.len())].copy_from_slice(&callsign_bytes);
+        buf[(3 + MAX_CONTACT_NAME_LENGTH)..(3 + MAX_CONTACT_NAME_LENGTH + name_bytes.len())]
+            .copy_from_slice(name_bytes);
+        let checksum = X25.checksum(&buf[..(CONTACT_LENGTH - 2)]);
+        buf[(CONTACT_LENGTH - 2)..].copy_from_slice(&checksum.to_le_bytes());
+
+        storage.write_data(start_addr, &buf);
     }
 }