diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-06-21 20:19:54 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2024-08-21 12:44:07 +0200 |
| commit | 160e1c38ceab0ae8876c2bf5f12438edd4d9b018 (patch) | |
| tree | 9eb219e82e95eb198e03bd9207cd08dd055a21a3 /examples/nrf9160 | |
| parent | aff66b9695a70222b20c19585f04df2ecbabccb1 (diff) | |
Add embassy-net-nrf91.
Diffstat (limited to 'examples/nrf9160')
| -rw-r--r-- | examples/nrf9160/.cargo/config.toml | 3 | ||||
| -rw-r--r-- | examples/nrf9160/Cargo.toml | 5 | ||||
| -rw-r--r-- | examples/nrf9160/memory.x | 8 | ||||
| -rw-r--r-- | examples/nrf9160/src/bin/modem_tcp_client.rs | 250 |
4 files changed, 263 insertions, 3 deletions
diff --git a/examples/nrf9160/.cargo/config.toml b/examples/nrf9160/.cargo/config.toml index f64c63966..6072b8595 100644 --- a/examples/nrf9160/.cargo/config.toml +++ b/examples/nrf9160/.cargo/config.toml | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | runner = "probe-rs run --chip nRF9160_xxAA" | 2 | # runner = "probe-rs run --chip nRF9160_xxAA" |
| 3 | runner = [ "probe-rs", "run", "--chip=nRF9160_xxAA", "--always-print-stacktrace", "--log-format={t} {[{L}]%bold} {s} {{c} {ff}:{l:1}%dimmed}" ] | ||
| 3 | 4 | ||
| 4 | [build] | 5 | [build] |
| 5 | target = "thumbv8m.main-none-eabihf" | 6 | target = "thumbv8m.main-none-eabihf" |
diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index c30b54ebd..fc24e99d2 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml | |||
| @@ -8,6 +8,8 @@ license = "MIT OR Apache-2.0" | |||
| 8 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 8 | embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 10 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 11 | embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } | ||
| 12 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } | ||
| 11 | 13 | ||
| 12 | defmt = "0.3" | 14 | defmt = "0.3" |
| 13 | defmt-rtt = "0.4" | 15 | defmt-rtt = "0.4" |
| @@ -15,6 +17,9 @@ defmt-rtt = "0.4" | |||
| 15 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | 17 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } |
| 16 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 17 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 19 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 20 | static_cell = { version = "2" } | ||
| 21 | embedded-io = "0.6.1" | ||
| 22 | embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } | ||
| 18 | 23 | ||
| 19 | [profile.release] | 24 | [profile.release] |
| 20 | debug = 2 | 25 | debug = 2 |
diff --git a/examples/nrf9160/memory.x b/examples/nrf9160/memory.x index 4c7d4ebf0..e33498773 100644 --- a/examples/nrf9160/memory.x +++ b/examples/nrf9160/memory.x | |||
| @@ -1,5 +1,9 @@ | |||
| 1 | MEMORY | 1 | MEMORY |
| 2 | { | 2 | { |
| 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | 3 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K |
| 4 | RAM : ORIGIN = 0x20018000, LENGTH = 160K | 4 | RAM : ORIGIN = 0x20010000, LENGTH = 192K |
| 5 | IPC : ORIGIN = 0x20000000, LENGTH = 64K | ||
| 5 | } | 6 | } |
| 7 | |||
| 8 | PROVIDE(__start_ipc = ORIGIN(IPC)); | ||
| 9 | PROVIDE(__end_ipc = ORIGIN(IPC) + LENGTH(IPC)); | ||
diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs new file mode 100644 index 000000000..b1dac18a1 --- /dev/null +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs | |||
| @@ -0,0 +1,250 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::ptr::addr_of_mut; | ||
| 6 | use core::str::FromStr; | ||
| 7 | use core::{slice, str}; | ||
| 8 | |||
| 9 | use defmt::{assert, *}; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; | ||
| 12 | use embassy_net_nrf91::{Runner, State}; | ||
| 13 | use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; | ||
| 14 | use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; | ||
| 15 | use embassy_nrf::uarte::Baudrate; | ||
| 16 | use embassy_nrf::{bind_interrupts, interrupt, peripherals, uarte}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use embedded_io_async::Write; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | #[interrupt] | ||
| 23 | fn IPC() { | ||
| 24 | embassy_net_nrf91::on_ipc_irq(); | ||
| 25 | } | ||
| 26 | |||
| 27 | bind_interrupts!(struct Irqs { | ||
| 28 | UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>; | ||
| 29 | }); | ||
| 30 | |||
| 31 | // embassy-net-nrf91 only supports blocking trace write for now. | ||
| 32 | // We don't want to block packet processing with slow uart writes, so | ||
| 33 | // we make an adapter that writes whatever fits in the buffer and drops | ||
| 34 | // data if it's full. | ||
| 35 | struct TraceWriter(BufferedUarteTx<'static, peripherals::SERIAL0>); | ||
| 36 | |||
| 37 | impl embedded_io::ErrorType for TraceWriter { | ||
| 38 | type Error = core::convert::Infallible; | ||
| 39 | } | ||
| 40 | |||
| 41 | impl embedded_io::Write for TraceWriter { | ||
| 42 | fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { | ||
| 43 | let _ = self.0.try_write(buf); | ||
| 44 | Ok(buf.len()) | ||
| 45 | } | ||
| 46 | fn flush(&mut self) -> Result<(), Self::Error> { | ||
| 47 | Ok(()) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[embassy_executor::task] | ||
| 52 | async fn modem_task(runner: Runner<'static, TraceWriter>) -> ! { | ||
| 53 | runner.run().await | ||
| 54 | } | ||
| 55 | |||
| 56 | #[embassy_executor::task] | ||
| 57 | async fn net_task(stack: &'static Stack<embassy_net_nrf91::NetDriver<'static>>) -> ! { | ||
| 58 | stack.run().await | ||
| 59 | } | ||
| 60 | |||
| 61 | #[embassy_executor::task] | ||
| 62 | async fn blink_task(pin: AnyPin) { | ||
| 63 | let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); | ||
| 64 | loop { | ||
| 65 | led.set_high(); | ||
| 66 | Timer::after_millis(100).await; | ||
| 67 | led.set_low(); | ||
| 68 | Timer::after_millis(100).await; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | extern "C" { | ||
| 73 | static __start_ipc: u8; | ||
| 74 | static __end_ipc: u8; | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::main] | ||
| 78 | async fn main(spawner: Spawner) { | ||
| 79 | let p = embassy_nrf::init(Default::default()); | ||
| 80 | |||
| 81 | info!("Hello World!"); | ||
| 82 | |||
| 83 | unwrap!(spawner.spawn(blink_task(p.P0_02.degrade()))); | ||
| 84 | |||
| 85 | let ipc_mem = unsafe { | ||
| 86 | let ipc_start = &__start_ipc as *const u8 as *mut MaybeUninit<u8>; | ||
| 87 | let ipc_end = &__end_ipc as *const u8 as *mut MaybeUninit<u8>; | ||
| 88 | let ipc_len = ipc_end.offset_from(ipc_start) as usize; | ||
| 89 | slice::from_raw_parts_mut(ipc_start, ipc_len) | ||
| 90 | }; | ||
| 91 | |||
| 92 | static mut TRACE_BUF: [u8; 4096] = [0u8; 4096]; | ||
| 93 | let mut config = uarte::Config::default(); | ||
| 94 | config.baudrate = Baudrate::BAUD1M; | ||
| 95 | let trace_writer = TraceWriter(BufferedUarteTx::new( | ||
| 96 | //let trace_uart = BufferedUarteTx::new( | ||
| 97 | unsafe { peripherals::SERIAL0::steal() }, | ||
| 98 | Irqs, | ||
| 99 | unsafe { peripherals::P0_01::steal() }, | ||
| 100 | //unsafe { peripherals::P0_14::steal() }, | ||
| 101 | config, | ||
| 102 | unsafe { &mut *addr_of_mut!(TRACE_BUF) }, | ||
| 103 | )); | ||
| 104 | |||
| 105 | static STATE: StaticCell<State> = StaticCell::new(); | ||
| 106 | let (device, control, runner) = embassy_net_nrf91::new(STATE.init(State::new()), ipc_mem, trace_writer).await; | ||
| 107 | unwrap!(spawner.spawn(modem_task(runner))); | ||
| 108 | |||
| 109 | let config = embassy_net::Config::default(); | ||
| 110 | |||
| 111 | // Generate "random" seed. nRF91 has no RNG, TODO figure out something... | ||
| 112 | let seed = 123456; | ||
| 113 | |||
| 114 | // Init network stack | ||
| 115 | static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); | ||
| 116 | static STACK: StaticCell<Stack<embassy_net_nrf91::NetDriver<'static>>> = StaticCell::new(); | ||
| 117 | let stack = &*STACK.init(Stack::new( | ||
| 118 | device, | ||
| 119 | config, | ||
| 120 | RESOURCES.init(StackResources::<2>::new()), | ||
| 121 | seed, | ||
| 122 | )); | ||
| 123 | |||
| 124 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 125 | |||
| 126 | control.wait_init().await; | ||
| 127 | info!("INIT OK"); | ||
| 128 | |||
| 129 | let mut buf = [0u8; 256]; | ||
| 130 | |||
| 131 | let n = control.at_command(b"AT+CFUN?", &mut buf).await; | ||
| 132 | info!("AT resp: '{}'", unsafe { str::from_utf8_unchecked(&buf[..n]) }); | ||
| 133 | |||
| 134 | let n = control | ||
| 135 | .at_command(b"AT+CGDCONT=0,\"IP\",\"iot.nat.es\"", &mut buf) | ||
| 136 | .await; | ||
| 137 | info!("AT resp: '{}'", unsafe { str::from_utf8_unchecked(&buf[..n]) }); | ||
| 138 | let n = control | ||
| 139 | .at_command(b"AT+CGAUTH=0,1,\"orange\",\"orange\"", &mut buf) | ||
| 140 | .await; | ||
| 141 | info!("AT resp: '{}'", unsafe { str::from_utf8_unchecked(&buf[..n]) }); | ||
| 142 | |||
| 143 | let n = control.at_command(b"AT+CFUN=1", &mut buf).await; | ||
| 144 | info!("AT resp: '{}'", unsafe { str::from_utf8_unchecked(&buf[..n]) }); | ||
| 145 | |||
| 146 | info!("waiting for attach..."); | ||
| 147 | loop { | ||
| 148 | Timer::after_millis(500).await; | ||
| 149 | let n = control.at_command(b"AT+CGATT?", &mut buf).await; | ||
| 150 | let mut res = &buf[..n]; | ||
| 151 | pop_prefix(&mut res, b"+CGATT: "); | ||
| 152 | let res = split_field(&mut res); | ||
| 153 | info!("AT resp field: '{}'", unsafe { str::from_utf8_unchecked(res) }); | ||
| 154 | if res == b"1" { | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | let n = control.at_command(b"AT+CGPADDR=0", &mut buf).await; | ||
| 160 | let mut res = &buf[..n]; | ||
| 161 | pop_prefix(&mut res, b"+CGPADDR: 0,"); | ||
| 162 | let ip = split_field(&mut res); | ||
| 163 | let ip = Ipv4Address::from_str(unsafe { str::from_utf8_unchecked(ip) }).unwrap(); | ||
| 164 | info!("IP: '{}'", ip); | ||
| 165 | |||
| 166 | info!("============== OPENING SOCKET"); | ||
| 167 | control.open_raw_socket().await; | ||
| 168 | |||
| 169 | stack.set_config_v4(embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 { | ||
| 170 | address: Ipv4Cidr::new(ip, 32), | ||
| 171 | gateway: None, | ||
| 172 | dns_servers: Default::default(), | ||
| 173 | })); | ||
| 174 | |||
| 175 | let mut rx_buffer = [0; 4096]; | ||
| 176 | let mut tx_buffer = [0; 4096]; | ||
| 177 | loop { | ||
| 178 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 179 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 180 | |||
| 181 | info!("Connecting..."); | ||
| 182 | let host_addr = embassy_net::Ipv4Address::from_str("83.51.182.206").unwrap(); | ||
| 183 | if let Err(e) = socket.connect((host_addr, 8000)).await { | ||
| 184 | warn!("connect error: {:?}", e); | ||
| 185 | continue; | ||
| 186 | } | ||
| 187 | info!("Connected to {:?}", socket.remote_endpoint()); | ||
| 188 | |||
| 189 | let msg = b"Hello world!\n"; | ||
| 190 | loop { | ||
| 191 | if let Err(e) = socket.write_all(msg).await { | ||
| 192 | warn!("write error: {:?}", e); | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | info!("txd: {}", core::str::from_utf8(msg).unwrap()); | ||
| 196 | Timer::after_secs(1).await; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | fn is_whitespace(char: u8) -> bool { | ||
| 202 | match char { | ||
| 203 | b'\r' | b'\n' | b' ' => true, | ||
| 204 | _ => false, | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | fn is_separator(char: u8) -> bool { | ||
| 209 | match char { | ||
| 210 | b',' | b'\r' | b'\n' | b' ' => true, | ||
| 211 | _ => false, | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | fn split_field<'a>(data: &mut &'a [u8]) -> &'a [u8] { | ||
| 216 | while !data.is_empty() && is_whitespace(data[0]) { | ||
| 217 | *data = &data[1..]; | ||
| 218 | } | ||
| 219 | |||
| 220 | if data.is_empty() { | ||
| 221 | return &[]; | ||
| 222 | } | ||
| 223 | |||
| 224 | if data[0] == b'"' { | ||
| 225 | let data2 = &data[1..]; | ||
| 226 | let end = data2.iter().position(|&x| x == b'"').unwrap_or(data2.len()); | ||
| 227 | let field = &data2[..end]; | ||
| 228 | let mut rest = &data2[data2.len().min(end + 1)..]; | ||
| 229 | if rest.first() == Some(&b'\"') { | ||
| 230 | rest = &rest[1..]; | ||
| 231 | } | ||
| 232 | while !rest.is_empty() && is_separator(rest[0]) { | ||
| 233 | rest = &rest[1..]; | ||
| 234 | } | ||
| 235 | *data = rest; | ||
| 236 | field | ||
| 237 | } else { | ||
| 238 | let end = data.iter().position(|&x| is_separator(x)).unwrap_or(data.len()); | ||
| 239 | let field = &data[0..end]; | ||
| 240 | let rest = &data[data.len().min(end + 1)..]; | ||
| 241 | *data = rest; | ||
| 242 | field | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | fn pop_prefix(data: &mut &[u8], prefix: &[u8]) { | ||
| 247 | assert!(data.len() >= prefix.len()); | ||
| 248 | assert!(&data[..prefix.len()] == prefix); | ||
| 249 | *data = &data[prefix.len()..]; | ||
| 250 | } | ||
