diff --git a/src/whisker/node_info.rs b/src/whisker/node_info.rs
index 73c8fd8ea96acc95ad9a27029d0f5864b1dbf6ba..7d4bd4727ca1ce57118f134c33435f87278e66a5 100644
--- a/src/whisker/node_info.rs
+++ b/src/whisker/node_info.rs
@@ -9,6 +9,11 @@ pub struct NodeInfo {
     voltage: Option<u8>,
     xcvr_temperature: Option<i8>,
     battery_charge: Option<u8>,
+    altitude: Option<f32>,
+    balloon: bool,
+    ambient_temperature: Option<i8>,
+    ambient_humidity: Option<u8>,
+    ambient_pressure: Option<u16>,
 }
 
 impl NodeInfo {
@@ -59,6 +64,32 @@ impl NodeInfo {
         self.battery_charge.map(|b| b as f64 / 2.55)
     }
 
+    /// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons.
+    /// Meters
+    pub fn altitude(&self) -> Option<f32> {
+        self.altitude
+    }
+
+    /// If true, this whisker was emitted by a high altitude balloon payload
+    pub fn is_balloon(&self) -> bool {
+        self.balloon
+    }
+
+    /// Degrees C
+    pub fn ambient_temperature(&self) -> Option<i8> {
+        self.ambient_temperature
+    }
+
+    /// Relative humidity, percent
+    pub fn ambient_humidity(&self) -> Option<f64> {
+        self.ambient_humidity.map(|x| x as f64 / 2.55)
+    }
+
+    /// Decapascal (daPa)
+    pub fn ambient_pressure(&self) -> Option<u16> {
+        self.ambient_pressure
+    }
+
     pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
         let mut bitmask: u32 = 0;
 
@@ -128,6 +159,38 @@ impl NodeInfo {
             i += 1;
         }
 
+        if let Some(x) = self.altitude {
+            bitmask |= 512;
+
+            buf.get_mut(i..(i + 4))?.copy_from_slice(&x.to_le_bytes());
+            i += 4;
+        }
+
+        if self.balloon {
+            bitmask |= 1024;
+        }
+
+        if let Some(x) = self.ambient_temperature {
+            bitmask |= 2048;
+
+            *buf.get_mut(i)? = x.to_le_bytes()[0];
+            i += 1;
+        }
+
+        if let Some(x) = self.ambient_humidity {
+            bitmask |= 4096;
+
+            *buf.get_mut(i)? = x;
+            i += 1;
+        }
+
+        if let Some(x) = self.ambient_pressure {
+            bitmask |= 8192;
+
+            buf.get_mut(i..(i + 2))?.copy_from_slice(&x.to_le_bytes());
+            i += 2;
+        }
+
         buf[0] = (i - 1).try_into().ok()?;
         buf[1..4].copy_from_slice(&bitmask.to_be_bytes()[1..]);
 
@@ -141,17 +204,17 @@ impl NodeInfo {
         let mut i = 4;
 
         if bitmask & 1 > 0 {
-            builder = builder.hardware_id(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?]));
+            builder.hardware_id = Some(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?]));
             i += 2;
         }
 
         if bitmask & 2 > 0 {
-            builder = builder.software_id(*data.get(i)?);
+            builder.software_id = Some(*data.get(i)?);
             i += 1;
         }
 
         if bitmask & 4 > 0 {
-            builder = builder.uptime(u32::from_le_bytes([
+            builder.uptime = Some(u32::from_le_bytes([
                 *data.get(i)?,
                 *data.get(i + 1)?,
                 *data.get(i + 2)?,
@@ -162,7 +225,7 @@ impl NodeInfo {
         }
 
         if bitmask & 8 > 0 {
-            builder = builder.antenna_height(*data.get(i)?);
+            builder.antenna_height = Some(*data.get(i)?);
             i += 1;
         }
 
@@ -182,14 +245,44 @@ impl NodeInfo {
         }
 
         if bitmask & 128 > 0 {
-            builder = builder.xcvr_temperature(i8::from_le_bytes([*data.get(i)?]));
+            builder.xcvr_temperature = Some(i8::from_le_bytes([*data.get(i)?]));
             i += 1;
         }
 
         if bitmask & 256 > 0 {
             builder.battery_charge = Some(*data.get(i)?);
+            i += 1;
         }
 
+        if bitmask & 512 > 0 {
+            builder.altitude = Some(f32::from_le_bytes(
+                data.get(i..(i + 4))?.try_into().unwrap(),
+            ));
+            i += 4;
+        }
+
+        builder.balloon = bitmask & 1024 > 0;
+
+        if bitmask & 2048 > 0 {
+            builder.ambient_temperature = Some(i8::from_le_bytes([*data.get(i)?]));
+            i += 1;
+        }
+
+        if bitmask & 4096 > 0 {
+            builder.ambient_humidity = Some(*data.get(i)?);
+            i += 1;
+        }
+
+        if bitmask & 8192 > 0 {
+            builder.ambient_pressure = Some(u16::from_le_bytes(
+                data.get(i..(i + 2))?.try_into().unwrap(),
+            ));
+            i += 2;
+        }
+
+        // prevent unused variable warning
+        let _ = i;
+
         Some(builder.build())
     }
 }
@@ -205,6 +298,11 @@ pub struct NodeInfoBuilder {
     voltage: Option<u8>,
     xcvr_temperature: Option<i8>,
     battery_charge: Option<u8>,
+    altitude: Option<f32>,
+    balloon: bool,
+    ambient_temperature: Option<i8>,
+    ambient_humidity: Option<u8>,
+    ambient_pressure: Option<u16>,
 }
 
 impl NodeInfoBuilder {
@@ -219,6 +317,11 @@ impl NodeInfoBuilder {
             voltage,
             xcvr_temperature,
             battery_charge,
+            altitude,
+            balloon,
+            ambient_temperature,
+            ambient_humidity,
+            ambient_pressure,
         } = self;
 
         NodeInfo {
@@ -231,6 +334,11 @@ impl NodeInfoBuilder {
             voltage,
             xcvr_temperature,
             battery_charge,
+            altitude,
+            balloon,
+            ambient_temperature,
+            ambient_humidity,
+            ambient_pressure,
         }
     }
 
@@ -294,6 +402,43 @@ impl NodeInfoBuilder {
 
         self
     }
+
+    /// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons.
+    /// Meters
+    pub fn altitude(mut self, val: f32) -> Self {
+        self.altitude = Some(val);
+
+        self
+    }
+
+    /// If true, this whisker was emitted by a high altitude balloon payload
+    /// Please only set this on actual balloon payloads. Otherwise it can cause issues for downstream users!
+    pub fn set_balloon(mut self) -> Self {
+        self.balloon = true;
+
+        self
+    }
+
+    /// Degrees C
+    pub fn ambient_temperature(mut self, val: i8) -> Self {
+        self.ambient_temperature = Some(val);
+
+        self
+    }
+
+    /// Relative humidity, percent
+    pub fn ambient_humidity(mut self, val: f64) -> Self {
+        self.ambient_humidity = Some((val * 2.55).min(255.0) as u8);
+
+        self
+    }
+
+    /// Decapascal (daPa)
+    pub fn ambient_pressure(mut self, val: u16) -> Self {
+        self.ambient_pressure = Some(val);
+
+        self
+    }
 }
 
 #[cfg(test)]