diff --git a/src/mavlink/reflection.rs b/src/mavlink/reflection.rs
index c9f8a7fc585cee4d3fae97cdd3124ee9fc85a22f..c7665364fda6ffb74bf642523e3ace510c722c64 100644
--- a/src/mavlink/reflection.rs
+++ b/src/mavlink/reflection.rs
@@ -7,6 +7,7 @@
 use std::collections::HashMap;
 
 use mavlink_bindgen::parser::{MavProfile, MavType};
+use skyward_mavlink::mavlink::Message;
 
 use crate::error::ErrInstrument;
 
@@ -39,12 +40,12 @@ impl ReflectionContext {
     }
 
     /// Get the name of a message by its ID.
-    pub fn get_msg(&self, msg: impl MessageLike) -> Option<&MavMessage> {
+    pub fn get_msg(&'static self, msg: impl MessageLike) -> Option<&'static MavMessage> {
         msg.to_mav_message(self).ok()
     }
 
     /// Get all field names for a message by its ID.
-    pub fn get_fields(&self, message_id: impl MessageLike) -> Option<Vec<IndexedField<'_>>> {
+    pub fn get_fields(&'static self, message_id: impl MessageLike) -> Option<Vec<IndexedField>> {
         message_id.to_mav_message(self).ok().map(|msg| {
             msg.fields
                 .iter()
@@ -72,9 +73,9 @@ impl ReflectionContext {
 
     /// Get all plottable field names for a message by its ID.
     pub fn get_plottable_fields(
-        &self,
+        &'static self,
         message_id: impl MessageLike,
-    ) -> Option<Vec<IndexedField<'_>>> {
+    ) -> Option<Vec<IndexedField>> {
         let msg = message_id.to_mav_message(self).ok()?;
         msg.fields
             .iter()
@@ -98,14 +99,36 @@ impl ReflectionContext {
     }
 }
 
-#[derive(Clone)]
-pub struct IndexedField<'a> {
+#[derive(Debug, Clone)]
+pub struct IndexedField {
     id: usize,
-    msg: &'a MavMessage,
-    field: &'a MavField,
+    msg: &'static MavMessage,
+    field: &'static MavField,
 }
 
-impl IndexedField<'_> {
+macro_rules! extract_as_type {
+    ($as_type: ty, $func: ident, $($mav_ty: ident, $rust_ty: ty),+) => {
+        pub fn $func(&self, message: &impl Message) -> Result<$as_type, String> {
+            macro_rules! downcast {
+                ($value: expr, $type: ty) => {
+                    Ok(*$value
+                        .downcast::<$type>()
+                        .map_err(|_| "Type mismatch".to_string())? as $as_type)
+                };
+            }
+
+            let value = message
+                .get_field(self.id)
+                .ok_or("Field not found".to_string())?;
+            match self.field.mavtype {
+                $(MavType::$mav_ty => downcast!(value, $rust_ty),)+
+                _ => Err("Field type not supported".to_string()),
+            }
+        }
+    };
+}
+
+impl IndexedField {
     pub fn msg(&self) -> &MavMessage {
         self.msg
     }
@@ -127,16 +150,136 @@ impl IndexedField<'_> {
     }
 }
 
+/// ### Extractors
+/// These methods allow to extract the value of a field from a message, casting
+/// it to the desired type.
+impl IndexedField {
+    #[rustfmt::skip]
+    extract_as_type!(f32, extract_as_f32,
+        UInt8, u8,
+        UInt16, u16,
+        UInt32, u32,
+        UInt64, u64,
+        Int8, i8,
+        Int16, i16,
+        Int32, i32,
+        Int64, i64,
+        Float, f32,
+        Double, f64
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(f64, extract_as_f64,
+        UInt8, u8,
+        UInt16, u16,
+        UInt32, u32,
+        UInt64, u64,
+        Int8, i8,
+        Int16, i16,
+        Int32, i32,
+        Int64, i64,
+        Float, f32,
+        Double, f64
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(u8, extract_as_u8,
+        UInt8, u8,
+        Char, char
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(u16, extract_as_u16,
+        UInt8, u8,
+        Int8, i8,
+        UInt16, u16
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(u32, extract_as_u32,
+        UInt8, u8,
+        Int8, i8,
+        UInt16, u16,
+        Int16, i16,
+        UInt32, u32
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(u64, extract_as_u64,
+        UInt8, u8,
+        Int8, i8,
+        UInt16, u16,
+        Int16, i16,
+        UInt32, u32,
+        Int32, i32,
+        UInt64, u64
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(i8, extract_as_i8,
+        Int8, i8
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(i16, extract_as_i16,
+        UInt8, u8,
+        Int8, i8,
+        Int16, i16
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(i32, extract_as_i32,
+        UInt8, u8,
+        Int8, i8,
+        UInt16, u16,
+        Int16, i16,
+        Int32, i32
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(i64, extract_as_i64,
+        UInt8, u8,
+        Int8, i8,
+        UInt16, u16,
+        Int16, i16,
+        UInt32, u32,
+        Int32, i32,
+        Int64, i64
+    );
+
+    #[rustfmt::skip]
+    extract_as_type!(char, extract_as_char,
+        UInt8, u8,
+        Char, char
+    );
+}
+
+impl std::hash::Hash for IndexedField {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.id.hash(state);
+        self.msg.id.hash(state);
+    }
+}
+
+impl PartialEq for IndexedField {
+    fn eq(&self, other: &Self) -> bool {
+        self.id == other.id && self.msg.id == other.msg.id
+    }
+}
+
 pub trait MessageLike {
-    fn to_mav_message<'b>(&self, ctx: &'b ReflectionContext) -> Result<&'b MavMessage, String>;
+    fn to_mav_message(
+        &self,
+        ctx: &'static ReflectionContext,
+    ) -> Result<&'static MavMessage, String>;
 }
 
-pub trait FieldLike<'a, 'b> {
+pub trait FieldLike {
     fn to_mav_field(
-        &'a self,
+        &self,
         msg_id: u32,
-        ctx: &'b ReflectionContext,
-    ) -> Result<IndexedField<'b>, String>;
+        ctx: &'static ReflectionContext,
+    ) -> Result<IndexedField, String>;
 }
 
 impl MessageLike for u32 {
@@ -158,12 +301,12 @@ impl MessageLike for &str {
     }
 }
 
-impl<'b> FieldLike<'_, 'b> for &MavField {
+impl FieldLike for &MavField {
     fn to_mav_field(
         &self,
         msg_id: u32,
-        ctx: &'b ReflectionContext,
-    ) -> Result<IndexedField<'b>, String> {
+        ctx: &'static ReflectionContext,
+    ) -> Result<IndexedField, String> {
         ctx.id_msg_map
             .get(&msg_id)
             .and_then(|msg| {
@@ -181,12 +324,8 @@ impl<'b> FieldLike<'_, 'b> for &MavField {
     }
 }
 
-impl<'b> FieldLike<'b, 'b> for IndexedField<'b> {
-    fn to_mav_field(
-        &self,
-        _msg_id: u32,
-        _ctx: &ReflectionContext,
-    ) -> Result<IndexedField<'_>, String> {
+impl FieldLike for IndexedField {
+    fn to_mav_field(&self, _msg_id: u32, _ctx: &ReflectionContext) -> Result<IndexedField, String> {
         Ok(IndexedField {
             id: self.id,
             msg: self.msg,
@@ -195,12 +334,12 @@ impl<'b> FieldLike<'b, 'b> for IndexedField<'b> {
     }
 }
 
-impl<'b> FieldLike<'_, 'b> for usize {
+impl FieldLike for usize {
     fn to_mav_field(
         &self,
         msg_id: u32,
-        ctx: &'b ReflectionContext,
-    ) -> Result<IndexedField<'b>, String> {
+        ctx: &'static ReflectionContext,
+    ) -> Result<IndexedField, String> {
         ctx.id_msg_map
             .get(&msg_id)
             .and_then(|msg| {
@@ -214,12 +353,12 @@ impl<'b> FieldLike<'_, 'b> for usize {
     }
 }
 
-impl<'b> FieldLike<'_, 'b> for &str {
+impl FieldLike for &str {
     fn to_mav_field(
         &self,
         msg_id: u32,
-        ctx: &'b ReflectionContext,
-    ) -> Result<IndexedField<'b>, String> {
+        ctx: &'static ReflectionContext,
+    ) -> Result<IndexedField, String> {
         ctx.id_msg_map
             .get(&msg_id)
             .and_then(|msg| {
diff --git a/src/ui/cache.rs b/src/ui/cache.rs
index 9835924607803fe30d82dc326ae6d8541970abb2..ec089b2e776d8574c08bfc2537ec7ec9739d242f 100644
--- a/src/ui/cache.rs
+++ b/src/ui/cache.rs
@@ -7,10 +7,7 @@ use std::{
     time::{Duration, Instant},
 };
 
-use egui::Context;
-use serialport::SerialPortInfo;
-
-use crate::{communication, error::ErrInstrument};
+use crate::error::ErrInstrument;
 
 const SERIAL_PORT_REFRESH_INTERVAL: Duration = Duration::from_millis(500);
 const SHORT_REFRESH_INTERVAL: Duration = Duration::from_millis(500);
diff --git a/src/ui/panes/plot.rs b/src/ui/panes/plot.rs
index 9d9ad095dedb335b8a35c6ad7c3c6a7532c037bf..01b58496e6829c9424f90bef3eb6bb8081bbca3d 100644
--- a/src/ui/panes/plot.rs
+++ b/src/ui/panes/plot.rs
@@ -5,16 +5,15 @@ use crate::{
     MAVLINK_PROFILE,
     error::ErrInstrument,
     mavlink::{
-        Message, MessageData, ROCKET_FLIGHT_TM_DATA, TimedMessage,
-        reflection::{self, FieldLike},
+        MessageData, ROCKET_FLIGHT_TM_DATA, TimedMessage,
+        reflection::{FieldLike, IndexedField},
     },
     ui::{app::PaneResponse, cache::ChangeTracker},
 };
 use egui::{Color32, Vec2b};
 use egui_plot::{Legend, Line, PlotPoint, PlotPoints};
 use egui_tiles::TileId;
-use mavlink_bindgen::parser::MavType;
-use serde::{Deserialize, Serialize};
+use serde::{self, Deserialize, Serialize, ser::SerializeStruct};
 use source_window::sources_window;
 use std::{hash::Hash, iter::zip};
 
@@ -65,7 +64,7 @@ impl PaneBehavior for Plot2DPane {
                         ))
                         .color(settings.color)
                         .width(settings.width)
-                        .name(&field.field.name),
+                        .name(&field.field().name),
                     );
                 }
                 plot_ui
@@ -104,10 +103,10 @@ impl PaneBehavior for Plot2DPane {
         } = &self.settings;
 
         for msg in messages {
-            let x: f64 = x_field.extract_from_message(&msg.message).log_unwrap();
+            let x: f64 = x_field.extract_as_f64(&msg.message).log_unwrap();
             let ys: Vec<f64> = y_fields
                 .iter()
-                .map(|(field, _)| field.extract_from_message(&msg.message).log_unwrap())
+                .map(|(field, _)| field.extract_as_f64(&msg.message).log_unwrap())
                 .collect();
 
             if self.line_data.len() < ys.len() {
@@ -115,7 +114,7 @@ impl PaneBehavior for Plot2DPane {
             }
 
             for (line, y) in zip(&mut self.line_data, ys) {
-                let point = if x_field.field.name == "timestamp" {
+                let point = if x_field.field().name == "timestamp" {
                     PlotPoint::new(x / 1e6, y)
                 } else {
                     PlotPoint::new(x, y)
@@ -146,15 +145,15 @@ fn show_menu(ui: &mut egui::Ui, settings_visible: &mut bool) {
     }
 }
 
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
 struct PlotSettings {
     plot_message_id: u32,
-    x_field: FieldWithID,
-    y_fields: Vec<(FieldWithID, LineSettings)>,
+    x_field: IndexedField,
+    y_fields: Vec<(IndexedField, LineSettings)>,
 }
 
 impl PlotSettings {
-    fn plot_lines(&self) -> &[(FieldWithID, LineSettings)] {
+    fn plot_lines(&self) -> &[(IndexedField, LineSettings)] {
         &self.y_fields
     }
 
@@ -166,19 +165,19 @@ impl PlotSettings {
         self.plot_message_id
     }
 
-    fn get_x_field(&self) -> &FieldWithID {
+    fn get_x_field(&self) -> &IndexedField {
         &self.x_field
     }
 
-    fn get_mut_x_field(&mut self) -> &mut FieldWithID {
+    fn get_mut_x_field(&mut self) -> &mut IndexedField {
         &mut self.x_field
     }
 
-    fn get_mut_y_fields(&mut self) -> &mut [(FieldWithID, LineSettings)] {
+    fn get_mut_y_fields(&mut self) -> &mut [(IndexedField, LineSettings)] {
         &mut self.y_fields[..]
     }
 
-    fn set_x_field(&mut self, field: FieldWithID) {
+    fn set_x_field(&mut self, field: IndexedField) {
         self.x_field = field;
     }
 
@@ -186,11 +185,11 @@ impl PlotSettings {
         self.y_fields.len()
     }
 
-    fn contains_field(&self, field: &FieldWithID) -> bool {
+    fn contains_field(&self, field: &IndexedField) -> bool {
         self.y_fields.iter().any(|(f, _)| f == field)
     }
 
-    fn add_field(&mut self, field: FieldWithID) {
+    fn add_field(&mut self, field: IndexedField) {
         let line_settings = LineSettings::default();
         self.y_fields.push((field, line_settings));
     }
@@ -198,8 +197,7 @@ impl PlotSettings {
     fn clear_fields(&mut self) {
         self.x_field = 0
             .to_mav_field(self.plot_message_id, &MAVLINK_PROFILE)
-            .log_unwrap()
-            .into();
+            .log_unwrap();
         self.y_fields.clear();
     }
 }
@@ -207,9 +205,9 @@ impl PlotSettings {
 impl Default for PlotSettings {
     fn default() -> Self {
         let msg_id = ROCKET_FLIGHT_TM_DATA::ID;
-        let x_field = FieldWithID::new(msg_id, 0).log_unwrap();
+        let x_field = 0.to_mav_field(msg_id, &MAVLINK_PROFILE).log_unwrap();
         let y_fields = vec![(
-            FieldWithID::new(msg_id, 1).log_unwrap(),
+            1.to_mav_field(msg_id, &MAVLINK_PROFILE).log_unwrap(),
             LineSettings::default(),
         )];
         Self {
@@ -250,65 +248,53 @@ impl Hash for LineSettings {
     }
 }
 
-/// A struct to hold a field and its ID in a message
-/// We use this and not `reflection::IndexedField` because we need to serialize it
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-struct FieldWithID {
-    id: usize,
-    field: reflection::MavField,
-}
-
-impl FieldWithID {
-    fn new(msg_id: u32, field_id: usize) -> Option<Self> {
-        Some(Self {
-            id: field_id,
-            field: field_id
-                .to_mav_field(msg_id, &MAVLINK_PROFILE)
-                .ok()?
-                .field()
-                .clone(),
-        })
+impl Serialize for PlotSettings {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        let mut state = serializer.serialize_struct("PlotSettings", 3)?;
+        let y_fields: Vec<_> = self.y_fields.iter().map(|(f, s)| (f.id(), s)).collect();
+        state.serialize_field("msg_id", &self.plot_message_id)?;
+        state.serialize_field("x_field", &self.x_field.id())?;
+        state.serialize_field("y_fields", &y_fields)?;
+        state.end()
     }
+}
 
-    fn extract_from_message(&self, message: &impl Message) -> Result<f64, String> {
-        macro_rules! downcast {
-            ($value: expr, $type: ty) => {
-                Ok(*$value
-                    .downcast::<$type>()
-                    .map_err(|_| "Type mismatch".to_string())? as f64)
-            };
+impl<'de> Deserialize<'de> for PlotSettings {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        #[derive(Deserialize)]
+        struct FieldSettings {
+            field: usize,
+            settings: LineSettings,
         }
 
-        let value = message
-            .get_field(self.id)
-            .ok_or("Field not found".to_string())?;
-        match self.field.mavtype {
-            MavType::UInt8 => downcast!(value, u8),
-            MavType::UInt16 => downcast!(value, u16),
-            MavType::UInt32 => downcast!(value, u32),
-            MavType::UInt64 => downcast!(value, u64),
-            MavType::Int8 => downcast!(value, i8),
-            MavType::Int16 => downcast!(value, i16),
-            MavType::Int32 => downcast!(value, i32),
-            MavType::Int64 => downcast!(value, i64),
-            MavType::Float => downcast!(value, f32),
-            MavType::Double => downcast!(value, f64),
-            _ => Err("Field type not supported".to_string()),
+        #[derive(Deserialize)]
+        struct PlotSettingsData {
+            msg_id: u32,
+            x_field: usize,
+            y_fields: Vec<FieldSettings>,
         }
-    }
-}
 
-impl Hash for FieldWithID {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.id.hash(state);
-    }
-}
-
-impl From<reflection::IndexedField<'_>> for FieldWithID {
-    fn from(indexed_field: reflection::IndexedField<'_>) -> Self {
-        Self {
-            id: indexed_field.id(),
-            field: indexed_field.field().clone(),
-        }
+        let data = PlotSettingsData::deserialize(deserializer)?;
+        let x_field = data
+            .x_field
+            .to_mav_field(data.msg_id, &MAVLINK_PROFILE)
+            .log_unwrap();
+        let y_fields = data
+            .y_fields
+            .into_iter()
+            .map(|FieldSettings { field, settings }| {
+                (
+                    field
+                        .to_mav_field(data.msg_id, &MAVLINK_PROFILE)
+                        .log_unwrap(),
+                    settings,
+                )
+            })
+            .collect();
+        Ok(Self {
+            plot_message_id: data.msg_id,
+            x_field,
+            y_fields,
+        })
     }
 }
diff --git a/src/ui/panes/plot/source_window.rs b/src/ui/panes/plot/source_window.rs
index e49df739c1d240ee93186a1c1fbd11ced4a389ec..bf636a781c86fabd5b02aebd589d870eb24db89d 100644
--- a/src/ui/panes/plot/source_window.rs
+++ b/src/ui/panes/plot/source_window.rs
@@ -1,7 +1,4 @@
-use crate::{
-    MAVLINK_PROFILE,
-    ui::{cache::ChangeTracker, panes::plot::FieldWithID},
-};
+use crate::{MAVLINK_PROFILE, ui::cache::ChangeTracker};
 
 use crate::error::ErrInstrument;
 
@@ -32,12 +29,9 @@ pub fn sources_window(ui: &mut egui::Ui, plot_settings: &mut PlotSettings) {
     }
 
     // check fields and assign a default field_x and field_y once the msg is changed
-    let fields: Vec<FieldWithID> = MAVLINK_PROFILE
+    let fields = MAVLINK_PROFILE
         .get_plottable_fields(plot_settings.get_msg_id())
-        .log_expect("Invalid message id")
-        .into_iter()
-        .map(|f| f.into())
-        .collect::<Vec<_>>();
+        .log_expect("Invalid message id");
     // get the first field that is in the list of fields or the previous if valid
     let x_field = plot_settings.get_x_field();
     let new_field_x = fields
@@ -57,10 +51,10 @@ pub fn sources_window(ui: &mut egui::Ui, plot_settings: &mut PlotSettings) {
 
     // if fields are valid, show the combo boxes for the x_axis
     egui::ComboBox::from_label("X Axis")
-        .selected_text(&x_field.field.name)
+        .selected_text(&x_field.field().name)
         .show_ui(ui, |ui| {
             for msg in fields.iter() {
-                ui.selectable_value(x_field, msg.to_owned(), &msg.field.name);
+                ui.selectable_value(x_field, msg.to_owned(), &msg.field().name);
             }
         });
 
@@ -85,10 +79,10 @@ pub fn sources_window(ui: &mut egui::Ui, plot_settings: &mut PlotSettings) {
                     "Y Axis".to_owned()
                 };
                 egui::ComboBox::from_label(widget_label)
-                    .selected_text(&field.field.name)
+                    .selected_text(&field.field().name)
                     .show_ui(ui, |ui| {
                         for msg in fields.iter() {
-                            ui.selectable_value(field, msg.to_owned(), &msg.field.name);
+                            ui.selectable_value(field, msg.to_owned(), &msg.field().name);
                         }
                     });
                 ui.color_edit_button_srgba(color);