Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

61 receive responses from a tcp server 2 #74

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cross/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ members = [
"dns",
"get_fw_version",
"join",
"send_data_tcp"
"send_data_tcp",
"receive_data_tcp"
]

[profile.dev]
Expand Down
42 changes: 42 additions & 0 deletions cross/receive_data_tcp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[package]
authors = [
"Jim Hodapp",
"Caleb Bourg",
"Glyn Matthews",
"Dilyn Corner"
]
edition = "2021"
name = "receive_data_tcp"
version = "0.3.0"
description = "Example RP2040 target application that demonstrates how to receive data from a remote server over TCP."

# makes `cargo check --all-targets` work
[[bin]]
name = "receive_data_tcp"
bench = false
doctest = false
test = false

[dependencies]
defmt = "0.3.0"
defmt-rtt = "0.3.1"
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = { version = "0.2", features=["unproven"] }
esp32-wroom-rp = { path = "../../esp32-wroom-rp" }
panic-probe = { version = "0.3.0", features = ["print-rtt"] }
heapless = "0.7.16"

rp2040-hal = { version = "0.6", features=["rt", "eh1_0_alpha"] }
rp2040-boot2 = { version = "0.2" }
fugit = "0.3"

[features]
default = ['defmt-default']
# these features are required by defmt
defmt-default = []
defmt-trace = []
defmt-debug = []
defmt-info = []
defmt-warn = []
defmt-error = []
214 changes: 214 additions & 0 deletions cross/receive_data_tcp/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//! # ESP32-WROOM-RP Pico Wireless Example
//!
//! This application demonstrates how to use the ESP32-WROOM-RP crate to
//! send data to a remote server over TCP.
//!
//! See the `Cargo.toml` file for Copyright and license details.

#![no_std]
#![no_main]

extern crate esp32_wroom_rp;

include!("../../secrets/secrets.rs");

// The macro for our start-up function
use cortex_m_rt::entry;

// Needed for debug output symbols to be linked in binary image
use defmt_rtt as _;

use panic_probe as _;

// Alias for our HAL crate
use rp2040_hal as hal;

use embedded_hal::spi::MODE_0;

use core::fmt::Write;

use fugit::RateExtU32;
use hal::gpio::{FloatingInput, PushPullOutput};
use hal::{clocks::Clock, pac};

use heapless::String;

use esp32_wroom_rp::{
gpio::EspControlPins, network::IpAddress, network::Port, network::TransportMode,
tcp_client::Connect, tcp_client::TcpClient, wifi::ConnectionStatus, wifi::Wifi,
};

const MAX_HTTP_DOC_LENGTH: usize = 4096 as usize;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;

/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
/// if your board has a different frequency
const XTAL_FREQ_HZ: u32 = 12_000_000u32;

/// Entry point to our bare-metal application.
///
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables are initialized.
#[entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();

// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

// Configure the clocks
let clocks = hal::clocks::init_clocks_and_plls(
XTAL_FREQ_HZ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();

let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);

// Set the pins to their default state
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);

defmt::info!("ESP32-WROOM-RP example to send data over TCP socket");

// These are implicitly used by the spi driver if they are in the correct mode
let _spi_miso = pins.gpio16.into_mode::<hal::gpio::FunctionSpi>();
let _spi_sclk = pins.gpio18.into_mode::<hal::gpio::FunctionSpi>();
let _spi_mosi = pins.gpio19.into_mode::<hal::gpio::FunctionSpi>();

let spi = hal::Spi::<_, _, 8>::new(pac.SPI0);

// Exchange the uninitialized SPI driver for an initialized one
let spi = spi.init(
&mut pac.RESETS,
clocks.peripheral_clock.freq(),
8.MHz(),
&MODE_0,
);

let esp_pins = EspControlPins {
// CS on pin x (GPIO7)
cs: pins.gpio7.into_mode::<PushPullOutput>(),
// GPIO0 on pin x (GPIO2)
gpio0: pins.gpio2.into_mode::<PushPullOutput>(),
// RESETn on pin x (GPIO11)
resetn: pins.gpio11.into_mode::<PushPullOutput>(),
// ACK on pin x (GPIO10)
ack: pins.gpio10.into_mode::<FloatingInput>(),
};

let mut wifi = Wifi::init(spi, esp_pins, &mut delay).unwrap();

let result = wifi.join(SSID, PASSPHRASE);
defmt::info!("Join Result: {:?}", result);

defmt::info!("Entering main loop");

let mut sleep: u32 = 1500;
loop {
match wifi.get_connection_status() {
Ok(status) => {
defmt::info!("Connection status: {:?}", status);
delay.delay_ms(sleep);

if status == ConnectionStatus::Connected {
defmt::info!("Connected to network: {:?}", SSID);

// The IPAddresses of two DNS servers to resolve hostnames with.
// Note that failover from ip1 to ip2 is fully functional.
let ip1: IpAddress = [9, 9, 9, 9];
let ip2: IpAddress = [8, 8, 8, 8];
let dns_result = wifi.set_dns(ip1, Some(ip2));

defmt::info!("set_dns result: {:?}", dns_result);

let hostname = "github.com";
// let ip_address: IpAddress = [140, 82, 114, 3]; // github.com

let port: Port = 80;
let mode: TransportMode = TransportMode::Tcp;

let mut http_document: String<MAX_HTTP_DOC_LENGTH> = String::from("");
// write!(http_document, "GET / HTTP/1.1\r\nHost: {}.{}.{}.{}:{}\r\nAccept: */*\r\n\r\n",
// ip_address[0],
// ip_address[1],
// ip_address[2],
// ip_address[3],
// port
// ).ok().unwrap();

write!(
http_document,
"GET / HTTP/1.1\r\nHost: {}:{}\r\nAccept: */*\r\n\r\n",
hostname, port
)
.ok()
.unwrap();

if let Err(e) = TcpClient::build(&mut wifi).connect(
hostname,
port,
mode,
&mut delay,
&mut |tcp_client| {
defmt::info!("TCP connection to {:?}:{:?} successful", hostname, port);
defmt::info!("Hostname: {:?}", tcp_client.server_hostname());
defmt::info!("Sending HTTP Document: {:?}", http_document.as_str());
match tcp_client.send_data(&http_document) {
Ok(result) => {
defmt::info!("Data sent successfully: {:?}", result);
defmt::info!("Receiving Response");

match tcp_client.receive_data() {
Ok(response) => { defmt::info!("Response: {:?}", response); }
Err(e) => { defmt::info!("Error receiving data: {:?}", e); }
}
}
Err(e) => {
defmt::error!("Response error: {:?}", e)
}
}
},
) {
defmt::error!(
"TCP connection to {:?}:{:?} failed: {:?}",
hostname,
port,
e
);
}

delay.delay_ms(100);

defmt::info!("Leaving network: {:?}", SSID);
wifi.leave().ok();
} else if status == ConnectionStatus::Disconnected {
sleep = 20000; // No need to loop as often after disconnecting
}
}
Err(e) => {
defmt::error!("Failed to get connection result: {:?}", e);
}
}
}
}
10 changes: 9 additions & 1 deletion esp32-wroom-rp/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub(crate) enum NinaCommand {
SetPassphrase = 0x11u8,
SetDNSConfig = 0x15u8,
GetConnStatus = 0x20u8,
AvailDataTcp = 0x2bu8,
StartClientTcp = 0x2du8,
StopClientTcp = 0x2eu8,
GetClientStateTcp = 0x2fu8,
Expand Down Expand Up @@ -392,7 +393,7 @@ pub(crate) trait ProtocolInterface {
fn get_conn_status(&mut self) -> Result<ConnectionStatus, Error>;
fn set_dns_config(&mut self, dns1: IpAddress, dns2: Option<IpAddress>) -> Result<(), Error>;
fn req_host_by_name(&mut self, hostname: &str) -> Result<u8, Error>;
fn get_host_by_name(&mut self) -> Result<[u8; MAX_NINA_RESPONSE_LENGTH], Error>;
fn get_host_by_name(&mut self) -> Result<NinaResponseBuffer, Error>;
Copy link
Member

@jhodapp jhodapp Feb 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea of mine, perhaps we could have a couple of different NinaResponseBuffer types:

  1. A shorter one, 255 bytes in length and maybe called SmallNinaResponseBuffer
  2. A larger one intended for things like HTTP response documents and maybe called LargeNinaResponseBuffer

The type that we always specify in these method definitions would implement a NinaResponseBuffer trait and then we could make the return type be a generic.

fn resolve(&mut self, hostname: &str) -> Result<IpAddress, Error>;
fn get_socket(&mut self) -> Result<Socket, Error>;
fn start_client_tcp(
Expand All @@ -405,6 +406,13 @@ pub(crate) trait ProtocolInterface {
fn stop_client_tcp(&mut self, socket: Socket, _mode: &TransportMode) -> Result<(), Error>;
fn get_client_state_tcp(&mut self, socket: Socket) -> Result<ConnectionState, Error>;
fn send_data(&mut self, data: &str, socket: Socket) -> Result<[u8; 1], Error>;
fn receive_data(&mut self, socket: Socket) -> Result<NinaResponseBuffer, Error>;
fn avail_data_tcp(&mut self, socket: Socket) -> Result<usize, Error>;
fn get_data_buf_tcp(
&mut self,
socket: Socket,
available_length: usize,
) -> Result<NinaResponseBuffer, Error>;
}

#[derive(Debug)]
Expand Down
Loading