aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-net-esp-hosted/CHANGELOG.md1
-rw-r--r--embassy-net-esp-hosted/src/control.rs40
-rw-r--r--embassy-net-esp-hosted/src/ioctl.rs27
-rw-r--r--embassy-net-esp-hosted/src/lib.rs5
-rw-r--r--embassy-net-esp-hosted/src/proto.rs12
5 files changed, 74 insertions, 11 deletions
diff --git a/embassy-net-esp-hosted/CHANGELOG.md b/embassy-net-esp-hosted/CHANGELOG.md
index d8b912295..6991b39fd 100644
--- a/embassy-net-esp-hosted/CHANGELOG.md
+++ b/embassy-net-esp-hosted/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11- Add an `Interface` trait to allow using other interface transports. 11- Add an `Interface` trait to allow using other interface transports.
12- Switch to `micropb` for protobuf. 12- Switch to `micropb` for protobuf.
13- Update protos to latest `esp-hosted-fg`. 13- Update protos to latest `esp-hosted-fg`.
14- Add support for OTA firmware updates.
14 15
15## 0.2.1 - 2025-08-26 16## 0.2.1 - 2025-08-26
16 17
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs
index 38ec648b4..eb79593f6 100644
--- a/embassy-net-esp-hosted/src/control.rs
+++ b/embassy-net-esp-hosted/src/control.rs
@@ -146,6 +146,43 @@ impl<'a> Control<'a> {
146 Ok(()) 146 Ok(())
147 } 147 }
148 148
149 /// Initiate a firmware update.
150 pub async fn ota_begin(&mut self) -> Result<(), Error> {
151 let req = proto::CtrlMsg_Req_OTABegin {};
152 ioctl!(self, ReqOtaBegin, RespOtaBegin, req, resp);
153 Ok(())
154 }
155
156 /// Write slice of firmware to a device.
157 ///
158 /// [`ota_begin`] must be called first.
159 ///
160 /// The slice is split into chunks that can be sent across
161 /// the ioctl protocol to the wifi adapter.
162 pub async fn ota_write(&mut self, data: &[u8]) -> Result<(), Error> {
163 for chunk in data.chunks(256) {
164 let req = proto::CtrlMsg_Req_OTAWrite {
165 ota_data: heapless::Vec::from_slice(chunk).unwrap(),
166 };
167 ioctl!(self, ReqOtaWrite, RespOtaWrite, req, resp);
168 }
169 Ok(())
170 }
171
172 /// End the OTA session.
173 ///
174 /// [`ota_begin`] must be called first.
175 ///
176 /// NOTE: Will reset the wifi adapter after 5 seconds.
177 pub async fn ota_end(&mut self) -> Result<(), Error> {
178 let req = proto::CtrlMsg_Req_OTAEnd {};
179 ioctl!(self, ReqOtaEnd, RespOtaEnd, req, resp);
180 self.shared.ota_done();
181 // Wait for re-init
182 self.init().await?;
183 Ok(())
184 }
185
149 /// duration in seconds, clamped to [10, 3600] 186 /// duration in seconds, clamped to [10, 3600]
150 async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { 187 async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
151 let req = proto::CtrlMsg_Req_ConfigHeartbeat { 188 let req = proto::CtrlMsg_Req_ConfigHeartbeat {
@@ -175,7 +212,8 @@ impl<'a> Control<'a> {
175 async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { 212 async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
176 debug!("ioctl req: {:?}", &msg); 213 debug!("ioctl req: {:?}", &msg);
177 214
178 let mut buf = [0u8; 128]; 215 // Theoretical max overhead is 29 bytes. Biggest message is OTA write with 256 bytes.
216 let mut buf = [0u8; 256 + 29];
179 let buf_len = buf.len(); 217 let buf_len = buf.len();
180 218
181 let mut encoder = PbEncoder::new(&mut buf[..]); 219 let mut encoder = PbEncoder::new(&mut buf[..]);
diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs
index a516f80c7..de0f867e8 100644
--- a/embassy-net-esp-hosted/src/ioctl.rs
+++ b/embassy-net-esp-hosted/src/ioctl.rs
@@ -23,16 +23,23 @@ pub struct Shared(RefCell<SharedInner>);
23 23
24struct SharedInner { 24struct SharedInner {
25 ioctl: IoctlState, 25 ioctl: IoctlState,
26 is_init: bool, 26 state: ControlState,
27 control_waker: WakerRegistration, 27 control_waker: WakerRegistration,
28 runner_waker: WakerRegistration, 28 runner_waker: WakerRegistration,
29} 29}
30 30
31#[derive(Clone, Copy)]
32pub(crate) enum ControlState {
33 Init,
34 Reboot,
35 Ready,
36}
37
31impl Shared { 38impl Shared {
32 pub fn new() -> Self { 39 pub fn new() -> Self {
33 Self(RefCell::new(SharedInner { 40 Self(RefCell::new(SharedInner {
34 ioctl: IoctlState::Done { resp_len: 0 }, 41 ioctl: IoctlState::Done { resp_len: 0 },
35 is_init: false, 42 state: ControlState::Init,
36 control_waker: WakerRegistration::new(), 43 control_waker: WakerRegistration::new(),
37 runner_waker: WakerRegistration::new(), 44 runner_waker: WakerRegistration::new(),
38 })) 45 }))
@@ -99,18 +106,30 @@ impl Shared {
99 } 106 }
100 } 107 }
101 108
109 // ota
110 pub fn ota_done(&self) {
111 let mut this = self.0.borrow_mut();
112 this.state = ControlState::Reboot;
113 }
114
102 // // // // // // // // // // // // // // // // // // // // 115 // // // // // // // // // // // // // // // // // // // //
116 //
117 // check if ota is in progress
118 pub(crate) fn state(&self) -> ControlState {
119 let this = self.0.borrow();
120 this.state
121 }
103 122
104 pub fn init_done(&self) { 123 pub fn init_done(&self) {
105 let mut this = self.0.borrow_mut(); 124 let mut this = self.0.borrow_mut();
106 this.is_init = true; 125 this.state = ControlState::Ready;
107 this.control_waker.wake(); 126 this.control_waker.wake();
108 } 127 }
109 128
110 pub fn init_wait(&self) -> impl Future<Output = ()> + '_ { 129 pub fn init_wait(&self) -> impl Future<Output = ()> + '_ {
111 poll_fn(|cx| { 130 poll_fn(|cx| {
112 let mut this = self.0.borrow_mut(); 131 let mut this = self.0.borrow_mut();
113 if this.is_init { 132 if let ControlState::Ready = this.state {
114 Poll::Ready(()) 133 Poll::Ready(())
115 } else { 134 } else {
116 this.control_waker.register(cx.waker()); 135 this.control_waker.register(cx.waker());
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs
index d882af8cf..2c7377281 100644
--- a/embassy-net-esp-hosted/src/lib.rs
+++ b/embassy-net-esp-hosted/src/lib.rs
@@ -234,6 +234,11 @@ where
234 tx_buf[..PayloadHeader::SIZE].fill(0); 234 tx_buf[..PayloadHeader::SIZE].fill(0);
235 } 235 }
236 Either4::Fourth(()) => { 236 Either4::Fourth(()) => {
237 // Extend the deadline if initializing
238 if let ioctl::ControlState::Reboot = self.shared.state() {
239 self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP;
240 continue;
241 }
237 panic!("heartbeat from esp32 stopped") 242 panic!("heartbeat from esp32 stopped")
238 } 243 }
239 } 244 }
diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs
index 74c67bd61..09bec8984 100644
--- a/embassy-net-esp-hosted/src/proto.rs
+++ b/embassy-net-esp-hosted/src/proto.rs
@@ -16,7 +16,7 @@ Switch to a proper script when https://github.com/YuhanLiin/micropb/issues/30 is
16 // Special config for things that need to be larger 16 // Special config for things that need to be larger
17 g.configure( 17 g.configure(
18 ".CtrlMsg_Req_OTAWrite.ota_data", 18 ".CtrlMsg_Req_OTAWrite.ota_data",
19 micropb_gen::Config::new().max_bytes(1024), 19 micropb_gen::Config::new().max_bytes(256),
20 ); 20 );
21 g.configure( 21 g.configure(
22 ".CtrlMsg_Event_ESPInit.init_data", 22 ".CtrlMsg_Event_ESPInit.init_data",
@@ -4296,28 +4296,28 @@ impl ::micropb::MessageEncode for CtrlMsg_Resp_OTABegin {
4296#[derive(Debug, Default, PartialEq, Clone)] 4296#[derive(Debug, Default, PartialEq, Clone)]
4297#[cfg_attr(feature = "defmt", derive(defmt::Format))] 4297#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4298pub struct CtrlMsg_Req_OTAWrite { 4298pub struct CtrlMsg_Req_OTAWrite {
4299 pub r#ota_data: ::micropb::heapless::Vec<u8, 1024>, 4299 pub r#ota_data: ::micropb::heapless::Vec<u8, 256>,
4300} 4300}
4301impl CtrlMsg_Req_OTAWrite { 4301impl CtrlMsg_Req_OTAWrite {
4302 ///Return a reference to `ota_data` 4302 ///Return a reference to `ota_data`
4303 #[inline] 4303 #[inline]
4304 pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 1024> { 4304 pub fn r#ota_data(&self) -> &::micropb::heapless::Vec<u8, 256> {
4305 &self.r#ota_data 4305 &self.r#ota_data
4306 } 4306 }
4307 ///Return a mutable reference to `ota_data` 4307 ///Return a mutable reference to `ota_data`
4308 #[inline] 4308 #[inline]
4309 pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 1024> { 4309 pub fn mut_ota_data(&mut self) -> &mut ::micropb::heapless::Vec<u8, 256> {
4310 &mut self.r#ota_data 4310 &mut self.r#ota_data
4311 } 4311 }
4312 ///Set the value of `ota_data` 4312 ///Set the value of `ota_data`
4313 #[inline] 4313 #[inline]
4314 pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> &mut Self { 4314 pub fn set_ota_data(&mut self, value: ::micropb::heapless::Vec<u8, 256>) -> &mut Self {
4315 self.r#ota_data = value.into(); 4315 self.r#ota_data = value.into();
4316 self 4316 self
4317 } 4317 }
4318 ///Builder method that sets the value of `ota_data`. Useful for initializing the message. 4318 ///Builder method that sets the value of `ota_data`. Useful for initializing the message.
4319 #[inline] 4319 #[inline]
4320 pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 1024>) -> Self { 4320 pub fn init_ota_data(mut self, value: ::micropb::heapless::Vec<u8, 256>) -> Self {
4321 self.r#ota_data = value.into(); 4321 self.r#ota_data = value.into();
4322 self 4322 self
4323 } 4323 }