rp2040_log_to_cdc/
main.rs

1#![no_std]
2#![no_main]
3
4use core::cell::RefCell;
5use core::ops::DerefMut;
6
7extern crate alloc;
8use alloc::boxed::Box;
9use alloc::{vec, vec::Vec};
10
11use rp2040_hal as hal;
12
13use hal::{
14    fugit::{ExtU64, RateExtU32},
15    timer::Instant,
16    uart::{DataBits, StopBits, UartConfig, UartPeripheral},
17    usb::UsbBus,
18    Clock,
19};
20
21use embedded_alloc::LlffHeap as Heap;
22use embedded_hal::digital::OutputPin;
23use embedded_io::Write;
24
25use usb_device::class_prelude::*;
26use usb_device::device::{StringDescriptors, UsbDeviceBuilder, UsbDeviceState, UsbVidPid};
27
28use bp35c0_j11::*;
29use route_b_secrets::*;
30
31#[global_allocator]
32static ALLOCATOR: Heap = Heap::empty();
33
34#[link_section = ".boot2"]
35#[no_mangle]
36#[used]
37static BOOT2_FIRMWARE: [u8; 256] = {
38    #[cfg(feature = "adafruit-feather-rp2040")]
39    {
40        rp2040_boot2::BOOT_LOADER_GD25Q64CS
41    }
42    #[cfg(feature = "rp-pico")]
43    {
44        rp2040_boot2::BOOT_LOADER_W25Q080
45    }
46};
47
48const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
49
50#[cfg(not(feature = "defmt"))]
51#[panic_handler]
52fn panic(_: &core::panic::PanicInfo) -> ! {
53    loop {}
54}
55
56#[cfg(feature = "defmt")]
57use {defmt_rtt as _, panic_probe as _};
58
59enum State<'a> {
60    Start,
61    ReleaseReset,
62    GetVersionInformation,
63    SetOperationModeForActiveScan,
64    ActiveScan,
65    SetOperationModeForRouteB(Channel),
66    SetRouteBPanaAuthenticationInformation,
67    StartRouteBOperation,
68    OpenUdpPort,
69    StartRouteBPana,
70    Loop(u64),
71
72    Wait(Instant, Box<dyn FnOnce() -> State<'a> + 'a>),
73    WaitForResponse(Box<dyn FnMut(Response) -> Option<State<'a>> + 'a>),
74    SendCommand {
75        buf: Vec<u8>,
76        sent: usize,
77        next_state: Box<State<'a>>,
78    },
79}
80
81fn configure_pins(
82    pins: hal::gpio::Pins,
83) -> (
84    impl hal::uart::ValidUartPinout<hal::pac::UART1>,
85    impl OutputPin,
86    impl OutputPin,
87) {
88    #[cfg(feature = "adafruit-feather-rp2040")]
89    return (
90        (pins.gpio24.into_function(), pins.gpio25.into_function()),
91        pins.gpio11.into_push_pull_output(),
92        pins.gpio10.into_push_pull_output(),
93    );
94
95    #[cfg(feature = "rp-pico")]
96    return (
97        (pins.gpio4.into_function(), pins.gpio5.into_function()),
98        pins.gpio10.into_push_pull_output(),
99        pins.gpio11.into_push_pull_output(),
100    );
101}
102
103#[hal::entry]
104fn main() -> ! {
105    {
106        use core::mem::MaybeUninit;
107        use core::ptr::addr_of_mut;
108        const HEAP_SIZE: usize = 8 * 1024;
109        static mut HEAP: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
110        unsafe { ALLOCATOR.init(addr_of_mut!(HEAP) as usize, HEAP_SIZE) }
111    }
112
113    let mut pac = hal::pac::Peripherals::take().unwrap();
114    let _core = hal::pac::CorePeripherals::take().unwrap();
115
116    let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
117
118    let clocks = hal::clocks::init_clocks_and_plls(
119        XOSC_CRYSTAL_FREQ,
120        pac.XOSC,
121        pac.CLOCKS,
122        pac.PLL_SYS,
123        pac.PLL_USB,
124        &mut pac.RESETS,
125        &mut watchdog,
126    )
127    .unwrap();
128
129    let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
130
131    let sio = hal::Sio::new(pac.SIO);
132    let pins = hal::gpio::Pins::new(
133        pac.IO_BANK0,
134        pac.PADS_BANK0,
135        sio.gpio_bank0,
136        &mut pac.RESETS,
137    );
138    let (pins_uart, mut pin_reset_n, mut pin_txs0108e_oe) = configure_pins(pins);
139
140    let mut uart = UartPeripheral::new(pac.UART1, pins_uart, &mut pac.RESETS)
141        .enable(
142            UartConfig::new(115_200.Hz(), DataBits::Eight, None, StopBits::One),
143            clocks.peripheral_clock.freq(),
144        )
145        .unwrap();
146
147    let usb_bus = UsbBusAllocator::new(UsbBus::new(
148        pac.USBCTRL_REGS,
149        pac.USBCTRL_DPRAM,
150        clocks.usb_clock,
151        true,
152        &mut pac.RESETS,
153    ));
154    let usb_cdc = RefCell::new(usbd_serial::SerialPort::new(&usb_bus));
155    let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
156        .strings(&[StringDescriptors::default().product("Serial port")])
157        .unwrap()
158        .device_class(usbd_serial::USB_CLASS_CDC)
159        .build();
160
161    let mut state = State::Start;
162
163    let mut parser = Parser::default();
164
165    loop {
166        let _ = usb_dev.poll(&mut [usb_cdc.borrow_mut().deref_mut()]);
167
168        if usb_dev.state() != UsbDeviceState::Configured {
169            continue;
170        }
171
172        let mut rx_buf = [0; 32];
173        if let Ok(len) = uart.read_raw(&mut rx_buf) {
174            for resp in rx_buf[0..len].iter().flat_map(|&c| parser.parse(c)) {
175                write!(usb_cdc.borrow_mut(), "[+] Rx: {resp:#x?}\r\n").unwrap();
176                if let State::WaitForResponse(ref mut f) = state {
177                    if let Some(new_state) = f(resp) {
178                        state = new_state;
179                    }
180                }
181            }
182        }
183
184        let send = |cmd, f| {
185            write!(usb_cdc.borrow_mut(), "[+] Tx: {cmd:#x?}\r\n").unwrap();
186            let mut buf = vec![0; 128];
187            let len = serialize_to_bytes(&cmd, &mut buf).unwrap();
188            buf.resize(len, 0);
189            State::SendCommand {
190                buf,
191                sent: 0,
192                next_state: Box::new(State::WaitForResponse(f)),
193            }
194        };
195
196        let tick = timer.get_counter();
197        match state {
198            State::Start => {
199                write!(
200                    usb_cdc.borrow_mut(),
201                    "\r\n\
202                    [+] ================================\r\n\
203                    [+] (✗╹◡╹)ノ {}\r\n\
204                    [+] ================================\r\n\
205                    \r\n",
206                    env!("CARGO_BIN_NAME")
207                )
208                .unwrap();
209
210                pin_txs0108e_oe.set_high().unwrap();
211                state = State::Wait(tick + 500.millis(), Box::new(|| State::ReleaseReset));
212            }
213
214            State::ReleaseReset => {
215                pin_reset_n.set_high().unwrap();
216                state = State::WaitForResponse(Box::new(|resp| match resp {
217                    Response::NotificationPoweredOn => {
218                        write!(usb_cdc.borrow_mut(), "[+] Ready\r\n").unwrap();
219                        Some(State::GetVersionInformation)
220                    }
221                    _ => None,
222                }));
223            }
224
225            State::GetVersionInformation => {
226                state = send(
227                    Command::GetVersionInformation,
228                    Box::new(|resp| match resp {
229                        Response::GetVersionInformation { .. } => {
230                            Some(State::SetOperationModeForActiveScan)
231                        }
232                        _ => None,
233                    }),
234                );
235            }
236
237            State::SetOperationModeForActiveScan => {
238                state = send(
239                    Command::SetOperationMode {
240                        mode: OperationMode::Dual,
241                        han_sleep: false,
242                        channel: Channel::Ch4F922p5MHz,
243                        tx_power: TxPower::P20mW,
244                    },
245                    Box::new(|resp| match resp {
246                        Response::SetOperationMode { .. } => Some(State::ActiveScan),
247                        _ => None,
248                    }),
249                );
250            }
251
252            State::ActiveScan => {
253                let mut scan_result = None;
254                state = send(
255                    Command::DoActiveScan {
256                        duration: ScanDuration::T616p96ms,
257                        mask_channels: 0x3fff0,
258                        pairing_id: Some(u64::from_be_bytes(
259                            ROUTE_B_ID[24..32].try_into().unwrap(),
260                        )),
261                    },
262                    Box::new(move |resp| match resp {
263                        Response::DoActiveScan { .. } => match scan_result.take() {
264                            Some(channel) => Some(State::SetOperationModeForRouteB(channel)),
265                            _ => Some(State::ActiveScan),
266                        },
267                        Response::NotificationActiveScan { channel, terminal } => {
268                            if !terminal.is_empty() {
269                                scan_result.replace(channel);
270                            }
271                            None
272                        }
273                        _ => None,
274                    }),
275                );
276            }
277
278            State::SetOperationModeForRouteB(channel) => {
279                state = send(
280                    Command::SetOperationMode {
281                        mode: OperationMode::Dual,
282                        han_sleep: false,
283                        channel,
284                        tx_power: TxPower::P20mW,
285                    },
286                    Box::new(|resp| match resp {
287                        Response::SetOperationMode { .. } => {
288                            Some(State::SetRouteBPanaAuthenticationInformation)
289                        }
290                        _ => None,
291                    }),
292                );
293            }
294
295            State::SetRouteBPanaAuthenticationInformation => {
296                state = send(
297                    Command::SetRouteBPanaAuthenticationInformation {
298                        id: ROUTE_B_ID,
299                        password: ROUTE_B_PASSWORD,
300                    },
301                    Box::new(|resp| match resp {
302                        Response::SetRouteBPanaAuthenticationInformation { .. } => {
303                            Some(State::StartRouteBOperation)
304                        }
305                        _ => None,
306                    }),
307                );
308            }
309
310            State::StartRouteBOperation => {
311                state = send(
312                    Command::StartRouteBOperation,
313                    Box::new(|resp| match resp {
314                        Response::StartRouteBOperation { result, .. } => {
315                            if result == 0x01 {
316                                Some(State::OpenUdpPort)
317                            } else {
318                                Some(State::StartRouteBOperation)
319                            }
320                        }
321                        _ => None,
322                    }),
323                );
324            }
325
326            State::OpenUdpPort => {
327                state = send(
328                    Command::OpenUdpPort(0x0e1a),
329                    Box::new(|resp| match resp {
330                        Response::OpenUdpPort { .. } => Some(State::StartRouteBPana),
331                        _ => None,
332                    }),
333                );
334            }
335
336            State::StartRouteBPana => {
337                state = send(
338                    Command::StartRouteBPana,
339                    Box::new(|resp| match resp {
340                        Response::StartRouteBPana { .. } => None,
341                        Response::NotificationPanaAuthentication {
342                            result: _,
343                            mac_address,
344                        } => Some(State::Loop(mac_address)),
345                        _ => None,
346                    }),
347                );
348            }
349
350            State::Loop(mac_address) => {
351                let destination_address = 0xfe800000000000000000000000000000_u128
352                    | (mac_address ^ 0x02000000_00000000) as u128;
353                state = State::Wait(
354                    tick + 10.secs(),
355                    Box::new(move || {
356                        send(
357                            Command::TransmitData {
358                                destination_address,
359                                source_port: 0x0e1a,
360                                destination_port: 0x0e1a,
361                                data: &[
362                                    0x10, // EHD1
363                                    0x81, // EHD2
364                                    0x00, 0x06, // TID
365                                    0x05, 0xff, 0x01, // SEOJ
366                                    0x02, 0x88, 0x01, // DEOJ
367                                    0x62, // ESV
368                                    0x04, // OPC
369                                    0x97, // EPC (現在時刻設定)
370                                    0x00, // PDC
371                                    0x98, // EPC (現在年月日設定)
372                                    0x00, // PDC
373                                    0xd3, // EPC (電力係数)
374                                    0x00, // PDC
375                                    0xe7, // EPC (瞬時電力計測値)
376                                    0x00, // PDC
377                                ],
378                            },
379                            Box::new(move |resp| match resp {
380                                Response::TransmitData { .. } => Some(State::Loop(mac_address)),
381                                _ => None,
382                            }),
383                        )
384                    }),
385                );
386            }
387
388            State::Wait(t, next_state) if tick >= t => {
389                state = next_state();
390            }
391
392            State::SendCommand {
393                buf,
394                sent,
395                next_state,
396            } => match uart.write(&buf[sent..]) {
397                Ok(len) => {
398                    if buf.len() >= sent + len {
399                        state = *next_state;
400                    } else {
401                        state = State::SendCommand {
402                            buf,
403                            sent: sent + len,
404                            next_state,
405                        };
406                    }
407                }
408                _ => todo!(),
409            },
410
411            _ => (),
412        }
413    }
414}