aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/nrf9160/.cargo/config.toml3
-rw-r--r--examples/nrf9160/Cargo.toml5
-rw-r--r--examples/nrf9160/memory.x8
-rw-r--r--examples/nrf9160/src/bin/modem_tcp_client.rs250
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"))']
2runner = "probe-rs run --chip nRF9160_xxAA" 2# runner = "probe-rs run --chip nRF9160_xxAA"
3runner = [ "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]
5target = "thumbv8m.main-none-eabihf" 6target = "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"
8embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 8embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 10embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
11embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] }
12embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] }
11 13
12defmt = "0.3" 14defmt = "0.3"
13defmt-rtt = "0.4" 15defmt-rtt = "0.4"
@@ -15,6 +17,9 @@ defmt-rtt = "0.4"
15cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
16cortex-m-rt = "0.7.0" 18cortex-m-rt = "0.7.0"
17panic-probe = { version = "0.3", features = ["print-defmt"] } 19panic-probe = { version = "0.3", features = ["print-defmt"] }
20static_cell = { version = "2" }
21embedded-io = "0.6.1"
22embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
18 23
19[profile.release] 24[profile.release]
20debug = 2 25debug = 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 @@
1MEMORY 1MEMORY
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
8PROVIDE(__start_ipc = ORIGIN(IPC));
9PROVIDE(__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
4use core::mem::MaybeUninit;
5use core::ptr::addr_of_mut;
6use core::str::FromStr;
7use core::{slice, str};
8
9use defmt::{assert, *};
10use embassy_executor::Spawner;
11use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources};
12use embassy_net_nrf91::{Runner, State};
13use embassy_nrf::buffered_uarte::{self, BufferedUarteTx};
14use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin};
15use embassy_nrf::uarte::Baudrate;
16use embassy_nrf::{bind_interrupts, interrupt, peripherals, uarte};
17use embassy_time::{Duration, Timer};
18use embedded_io_async::Write;
19use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _};
21
22#[interrupt]
23fn IPC() {
24 embassy_net_nrf91::on_ipc_irq();
25}
26
27bind_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.
35struct TraceWriter(BufferedUarteTx<'static, peripherals::SERIAL0>);
36
37impl embedded_io::ErrorType for TraceWriter {
38 type Error = core::convert::Infallible;
39}
40
41impl 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]
52async fn modem_task(runner: Runner<'static, TraceWriter>) -> ! {
53 runner.run().await
54}
55
56#[embassy_executor::task]
57async fn net_task(stack: &'static Stack<embassy_net_nrf91::NetDriver<'static>>) -> ! {
58 stack.run().await
59}
60
61#[embassy_executor::task]
62async 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
72extern "C" {
73 static __start_ipc: u8;
74 static __end_ipc: u8;
75}
76
77#[embassy_executor::main]
78async 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
201fn is_whitespace(char: u8) -> bool {
202 match char {
203 b'\r' | b'\n' | b' ' => true,
204 _ => false,
205 }
206}
207
208fn is_separator(char: u8) -> bool {
209 match char {
210 b',' | b'\r' | b'\n' | b' ' => true,
211 _ => false,
212 }
213}
214
215fn 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
246fn 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}