crazyflie_lib/subsystems/memory/
eeprom_config.rs

1use crate::{subsystems::memory::{memory_types, MemoryBackend}, Error, Result};
2use std::{
3    convert::{TryFrom, TryInto},
4    fmt::{self, Display},
5};
6
7use memory_types::{FromMemoryBackend, MemoryType};
8
9/// Describes the content of the I2C EEPROM used for configuration on the Crazyflie
10/// platform.
11#[derive(Debug)]
12pub struct EEPROMConfigMemory {
13  memory: MemoryBackend,
14  /// Version of the EEPROM configuration structure.
15  version: u8,
16  /// Radio frequency channel (0-125) for wireless communication.
17  radio_channel: u8,
18  /// Data transmission rate for radio communication.
19  radio_speed: RadioSpeed,
20  /// Forward/backward balance adjustment in degrees.
21  pitch_trim: f32,
22  /// Left/right balance adjustment in degrees.
23  roll_trim: f32,
24  /// 5-byte unique address for the radio module.
25  radio_address: [u8; 5],
26}
27
28
29/// Represents the radio speed settings available for the Crazyflie.
30#[derive(Debug, Clone)]
31pub enum RadioSpeed {
32  /// 250 Kbps data rate
33  R250Kbps = 0,
34  /// 1 Mbps data rate
35  R1Mbps = 1,
36  /// 2 Mbps data rate
37  R2Mbps = 2,
38}
39
40impl TryFrom<u8> for RadioSpeed {
41  type Error = Error;
42
43  fn try_from(value: u8) -> Result<Self> {
44    match value {
45      0 => Ok(RadioSpeed::R250Kbps),
46      1 => Ok(RadioSpeed::R1Mbps),
47      2 => Ok(RadioSpeed::R2Mbps),
48      _ => Err(Error::MemoryError(format!("Invalid radio speed value: {}", value))),
49    }
50  }
51}
52
53impl Display for RadioSpeed {
54  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55    match self {
56      RadioSpeed::R250Kbps => write!(f, "250 kbps"),
57      RadioSpeed::R1Mbps => write!(f, "1 Mbps"),
58      RadioSpeed::R2Mbps => write!(f, "2 Mbps"),
59    }
60  }
61}
62
63impl FromMemoryBackend for EEPROMConfigMemory {
64    async fn from_memory_backend(memory: MemoryBackend) -> Result<Self> {
65        if memory.memory_type == MemoryType::EEPROMConfig {
66            Ok(EEPROMConfigMemory::new(memory).await?)
67        } else {
68            Err(Error::MemoryError("Wrong type of memory!".to_owned()))
69        }
70    }
71
72    async fn initialize_memory_backend(memory: MemoryBackend) -> Result<Self> {
73        if memory.memory_type == MemoryType::EEPROMConfig {
74            Ok(EEPROMConfigMemory::initialize(memory).await?)
75        } else {
76            Err(Error::MemoryError("Wrong type of memory!".to_owned()))
77        }
78    }
79
80    fn close_memory(self) -> MemoryBackend {
81      self.memory
82    }
83}
84
85impl EEPROMConfigMemory {
86    pub(crate) async fn new(memory: MemoryBackend) -> Result<Self> {
87        let data = memory.read::<fn(usize, usize)>(0, 21, None).await?;
88        if data.len() >= 4 && &data[0..4] == b"0xBC" {
89            let version = data[4];
90            let radio_channel = data[5];
91            let radio_speed = RadioSpeed::try_from(data[6])?;
92            let pitch_trim = f32::from_le_bytes(data[7..11].try_into().unwrap());
93            let roll_trim = f32::from_le_bytes(data[11..15].try_into().unwrap());
94            let mut radio_address = [0; 5];
95            radio_address.copy_from_slice(&data[15..20]);
96
97            let calculated_checksum = data[..data.len()-1].iter().fold(0u8, |acc, &byte| acc.wrapping_add(byte));
98            let stored_checksum = data[data.len()-1];
99
100            if calculated_checksum != stored_checksum {
101              return Err(Error::MemoryError("Checksum mismatch in EEPROM config data".to_string()));
102            }
103
104            Ok(EEPROMConfigMemory {
105                memory,
106                version,
107                radio_channel,
108                radio_speed,
109                pitch_trim,
110                roll_trim,
111                radio_address,
112            })
113        } else {
114            Err(Error::MemoryError(
115                "Malformed EEPROM config data".to_string(),
116            ))
117        }
118    }
119
120    pub(crate) async fn initialize(memory: MemoryBackend) -> Result<Self> {
121      Ok(EEPROMConfigMemory {
122        memory,
123        version: 0,
124        radio_channel: 80,
125        radio_speed: RadioSpeed::R2Mbps,
126        pitch_trim: 0.0,
127        roll_trim: 0.0,
128        radio_address: [0xE7, 0xE7, 0xE7, 0xE7, 0xE7],
129      })
130    }
131
132    /// Commit the current configuration back to the EEPROM.
133    /// This will overwrite the existing configuration.
134    /// 
135    /// Returns `Ok(())` if the operation is successful, or an `Error` if it fails.
136    /// # Errors
137    /// This function will return an error if the write operation to the EEPROM fails.
138    pub async fn commit(&self) -> Result<()> {
139      let mut data = Vec::new();
140      data.extend_from_slice(b"0xBC");
141      data.push(self.version);
142      data.push(self.radio_channel);
143      data.push(self.radio_speed.clone() as u8);
144      data.extend_from_slice(&self.pitch_trim.to_le_bytes());
145      data.extend_from_slice(&self.roll_trim.to_le_bytes());
146      data.extend_from_slice(&self.radio_address);
147
148      let checksum = data.iter().fold(0u8, |acc, &byte| acc.wrapping_add(byte));
149      data.push(checksum);
150
151      self.memory.write::<fn(usize, usize)>(0, &data, None).await
152    }
153
154
155  /// Gets the radio frequency channel.
156  pub fn get_radio_channel(&self) -> u8 {
157    self.radio_channel
158  }
159
160  /// Sets the radio frequency channel (0-125).
161  pub fn set_radio_channel(&mut self, channel: u8) -> Result<()> {
162    if channel > 125 {
163      return Err(Error::InvalidParameter("Radio channel must be between 0 and 125".into()));
164    }
165    self.radio_channel = channel;
166    Ok(())
167  }
168
169  /// Gets the radio speed.
170  pub fn get_radio_speed(&self) -> &RadioSpeed {
171    &self.radio_speed
172  }
173
174  /// Sets the radio speed.
175  pub fn set_radio_speed(&mut self, speed: RadioSpeed) {
176    self.radio_speed = speed;
177  }
178
179  /// Gets the pitch trim value.
180  pub fn get_pitch_trim(&self) -> f32 {
181    self.pitch_trim
182  }
183
184  /// Sets the pitch trim value.
185  pub fn set_pitch_trim(&mut self, trim: f32) {
186    self.pitch_trim = trim;
187  }
188
189  /// Gets the roll trim value.
190  pub fn get_roll_trim(&self) -> f32 {
191    self.roll_trim
192  }
193
194  /// Sets the roll trim value.
195  pub fn set_roll_trim(&mut self, trim: f32) {
196    self.roll_trim = trim;
197  }
198
199  /// Gets the radio address.
200  pub fn get_radio_address(&self) -> &[u8; 5] {
201    &self.radio_address
202  }
203
204  /// Sets the radio address.
205  pub fn set_radio_address(&mut self, address: [u8; 5]) {
206    self.radio_address = address;
207  }
208}