forked from M-Labs/ionpak-thermostat
integrate ethmac/smoltcp (timestamp missing), add HTTP server
This commit is contained in:
parent
98f116e226
commit
648b4da9da
|
@ -320,3 +320,13 @@ pub fn init() {
|
|||
adc0.actss.write(|w| w.asen0().bit(true));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_mac_address() -> [u8; 6] {
|
||||
let (userreg0, userreg1) = cortex_m::interrupt::free(|cs| {
|
||||
let flashctl = tm4c129x::FLASH_CTRL.borrow(cs);
|
||||
(flashctl.userreg0.read().bits(),
|
||||
flashctl.userreg1.read().bits())
|
||||
});
|
||||
[userreg0 as u8, (userreg0 >> 8) as u8, (userreg0 >> 16) as u8,
|
||||
userreg1 as u8, (userreg1 >> 8) as u8, (userreg1 >> 16) as u8]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
use core::fmt;
|
||||
|
||||
const MAX_PATH: usize = 128;
|
||||
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
|
||||
enum State {
|
||||
WaitG,
|
||||
WaitE,
|
||||
WaitT,
|
||||
WaitSpace,
|
||||
GetPath,
|
||||
WaitCR1,
|
||||
WaitLF1,
|
||||
WaitCR2,
|
||||
WaitLF2,
|
||||
Finished
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
state: State,
|
||||
path_idx: usize,
|
||||
path: [u8; MAX_PATH]
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new() -> Request {
|
||||
Request {
|
||||
state: State::WaitG,
|
||||
path_idx: 0,
|
||||
path: [0; MAX_PATH]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.state = State::WaitG;
|
||||
self.path_idx = 0;
|
||||
}
|
||||
|
||||
pub fn input_char(&mut self, c: u8) -> Result<bool, &'static str> {
|
||||
match self.state {
|
||||
State::WaitG => {
|
||||
if c == b'G' {
|
||||
self.state = State::WaitE;
|
||||
} else {
|
||||
return Err("invalid character in method")
|
||||
}
|
||||
}
|
||||
State::WaitE => {
|
||||
if c == b'E' {
|
||||
self.state = State::WaitT;
|
||||
} else {
|
||||
return Err("invalid character in method")
|
||||
}
|
||||
}
|
||||
State::WaitT => {
|
||||
if c == b'T' {
|
||||
self.state = State::WaitSpace;
|
||||
} else {
|
||||
return Err("invalid character in method")
|
||||
}
|
||||
}
|
||||
State::WaitSpace => {
|
||||
if c == b' ' {
|
||||
self.state = State::GetPath;
|
||||
} else {
|
||||
return Err("invalid character in method")
|
||||
}
|
||||
}
|
||||
State::GetPath => {
|
||||
if c == b'\r' || c == b'\n' {
|
||||
return Err("GET ended prematurely")
|
||||
} else if c == b' ' {
|
||||
if self.path_idx == 0 {
|
||||
return Err("path is empty")
|
||||
} else {
|
||||
self.state = State::WaitCR1;
|
||||
}
|
||||
} else {
|
||||
if self.path_idx >= self.path.len() {
|
||||
return Err("path is too long")
|
||||
} else {
|
||||
self.path[self.path_idx] = c;
|
||||
self.path_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
State::WaitCR1 => {
|
||||
if c == b'\r' {
|
||||
self.state = State::WaitLF1;
|
||||
}
|
||||
}
|
||||
State::WaitLF1 => {
|
||||
if c == b'\n' {
|
||||
self.state = State::WaitCR2;
|
||||
} else {
|
||||
self.state = State::WaitCR1;
|
||||
}
|
||||
}
|
||||
State::WaitCR2 => {
|
||||
if c == b'\r' {
|
||||
self.state = State::WaitLF2;
|
||||
} else {
|
||||
self.state = State::WaitCR1;
|
||||
}
|
||||
}
|
||||
State::WaitLF2 => {
|
||||
if c == b'\n' {
|
||||
self.state = State::Finished;
|
||||
return Ok(true)
|
||||
} else {
|
||||
self.state = State::WaitCR1;
|
||||
}
|
||||
}
|
||||
State::Finished => return Err("trailing characters")
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn input(&mut self, buf: &[u8]) -> Result<bool, &'static str> {
|
||||
let mut result = Ok(false);
|
||||
for c in buf.iter() {
|
||||
result = self.input_char(*c);
|
||||
if result.is_err() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn get_path<'a>(&'a self) -> Result<&'a [u8], &'static str> {
|
||||
if self.state != State::Finished {
|
||||
return Err("request is not finished")
|
||||
}
|
||||
Ok(&self.path[..self.path_idx])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_reply_header(output: &mut fmt::Write, status: u16, content_type: &str, gzip: bool) -> fmt::Result {
|
||||
let status_text = match status {
|
||||
200 => "OK",
|
||||
404 => "Not Found",
|
||||
500 => "Internal Server Error",
|
||||
_ => return Err(fmt::Error)
|
||||
};
|
||||
write!(output, "HTTP/1.1 {} {}\r\nContent-Type: {}\r\n",
|
||||
status, status_text, content_type)?;
|
||||
if gzip {
|
||||
write!(output, "Content-Encoding: gzip\r\n")?;
|
||||
}
|
||||
write!(output, "\r\n")
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>ionpak</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<body>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="w3-sidebar w3-light-grey w3-bar-block" style="width:15%">
|
||||
<h3 class="w3-bar-item"><img src="logo.svg"></h3>
|
||||
<a href="/" class="w3-bar-item w3-button">Measure</a>
|
||||
<a href="/gauge_settings.html" class="w3-bar-item w3-button">Gauge settings</a>
|
||||
<a href="/network_settings.html" class="w3-bar-item w3-button">Network settings</a>
|
||||
</div>
|
||||
|
||||
<!-- Page Content --->
|
||||
<div style="margin-left:15%">
|
||||
|
||||
<div class="w3-container w3-teal">
|
||||
<h1>Measure</h1>
|
||||
</div>
|
||||
|
||||
<div class="w3-container">
|
||||
<p>Pressure:</p>
|
||||
</div>
|
||||
<div class="w3-container w3-xxlarge">
|
||||
2.3×10<sup>-7</sup> mbar
|
||||
</div>
|
||||
|
||||
<div class="w3-container">
|
||||
<p>
|
||||
At local time:
|
||||
<script language="Javascript">
|
||||
var date = new Date();
|
||||
var n = date.toDateString();
|
||||
var time = date.toLocaleString();
|
||||
document.write(time);
|
||||
</script>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
|
@ -12,6 +12,11 @@ use cortex_m::exception::Handlers as ExceptionHandlers;
|
|||
use cortex_m::interrupt::Mutex;
|
||||
use tm4c129x::interrupt::Interrupt;
|
||||
use tm4c129x::interrupt::Handlers as InterruptHandlers;
|
||||
use smoltcp::Error;
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress};
|
||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
||||
use smoltcp::socket::{AsSocket, SocketSet};
|
||||
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
|
@ -34,6 +39,8 @@ mod pid;
|
|||
mod loop_anode;
|
||||
mod loop_cathode;
|
||||
mod electrometer;
|
||||
mod http;
|
||||
mod pages;
|
||||
|
||||
static TIME: Mutex<Cell<u64>> = Mutex::new(Cell::new(0));
|
||||
|
||||
|
@ -68,6 +75,26 @@ impl fmt::Write for UART0 {
|
|||
}
|
||||
}
|
||||
|
||||
const TCP_RX_BUFFER_SIZE: usize = 256;
|
||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
|
||||
macro_rules! create_socket_storage {
|
||||
($rx_storage:ident, $tx_storage:ident) => (
|
||||
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
||||
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! create_socket {
|
||||
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:ident) => (
|
||||
let tcp_rx_buffer = TcpSocketBuffer::new(&mut $rx_storage[..]);
|
||||
let tcp_tx_buffer = TcpSocketBuffer::new(&mut $tx_storage[..]);
|
||||
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let $target = $set.add(tcp_socket);
|
||||
)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Enable the FPU
|
||||
unsafe {
|
||||
|
@ -132,17 +159,95 @@ fn main_with_fpu() {
|
|||
|_|\___/|_| |_| .__/ \__,_|_|\_\
|
||||
| |
|
||||
|_|
|
||||
Ready."#);
|
||||
"#);
|
||||
|
||||
let hardware_addr = EthernetAddress(board::get_mac_address());
|
||||
let mut protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
|
||||
println!("MAC {} IP {}", hardware_addr, protocol_addrs[0]);
|
||||
let mut arp_cache_entries: [_; 8] = Default::default();
|
||||
let mut arp_cache = SliceArpCache::new(&mut arp_cache_entries[..]);
|
||||
ethmac::init(hardware_addr.0);
|
||||
let mut device = ethmac::EthernetDevice;
|
||||
let mut iface = EthernetInterface::new(
|
||||
&mut device, &mut arp_cache as &mut ArpCache,
|
||||
hardware_addr, &mut protocol_addrs[..]);
|
||||
|
||||
create_socket_storage!(tcp_rx_storage0, tcp_tx_storage0);
|
||||
create_socket_storage!(tcp_rx_storage1, tcp_tx_storage1);
|
||||
create_socket_storage!(tcp_rx_storage2, tcp_tx_storage2);
|
||||
create_socket_storage!(tcp_rx_storage3, tcp_tx_storage3);
|
||||
create_socket_storage!(tcp_rx_storage4, tcp_tx_storage4);
|
||||
create_socket_storage!(tcp_rx_storage5, tcp_tx_storage5);
|
||||
create_socket_storage!(tcp_rx_storage6, tcp_tx_storage6);
|
||||
create_socket_storage!(tcp_rx_storage7, tcp_tx_storage7);
|
||||
|
||||
let mut socket_set_entries: [_; 8] = Default::default();
|
||||
let mut sockets = SocketSet::new(&mut socket_set_entries[..]);
|
||||
|
||||
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle0);
|
||||
create_socket!(sockets, tcp_rx_storage1, tcp_tx_storage1, tcp_handle1);
|
||||
create_socket!(sockets, tcp_rx_storage2, tcp_tx_storage2, tcp_handle2);
|
||||
create_socket!(sockets, tcp_rx_storage3, tcp_tx_storage3, tcp_handle3);
|
||||
create_socket!(sockets, tcp_rx_storage4, tcp_tx_storage4, tcp_handle4);
|
||||
create_socket!(sockets, tcp_rx_storage5, tcp_tx_storage5, tcp_handle5);
|
||||
create_socket!(sockets, tcp_rx_storage6, tcp_tx_storage6, tcp_handle6);
|
||||
create_socket!(sockets, tcp_rx_storage7, tcp_tx_storage7, tcp_handle7);
|
||||
|
||||
let mut sessions = [
|
||||
(http::Request::new(), tcp_handle0),
|
||||
(http::Request::new(), tcp_handle1),
|
||||
(http::Request::new(), tcp_handle2),
|
||||
(http::Request::new(), tcp_handle3),
|
||||
(http::Request::new(), tcp_handle4),
|
||||
(http::Request::new(), tcp_handle5),
|
||||
(http::Request::new(), tcp_handle6),
|
||||
(http::Request::new(), tcp_handle7),
|
||||
];
|
||||
|
||||
let mut next_blink = 0;
|
||||
let mut next_info = 0;
|
||||
let mut led_state = true;
|
||||
let mut latch_reset_time = None;
|
||||
loop {
|
||||
board::process_errors();
|
||||
|
||||
let time = get_time();
|
||||
|
||||
for &mut(ref mut request, ref tcp_handle) in sessions.iter_mut() {
|
||||
let socket: &mut TcpSocket = sockets.get_mut(*tcp_handle).as_socket();
|
||||
if !socket.is_open() {
|
||||
socket.listen(80).unwrap()
|
||||
}
|
||||
|
||||
if socket.may_recv() {
|
||||
let request_status = {
|
||||
let data = socket.recv(TCP_RX_BUFFER_SIZE).unwrap();
|
||||
request.input(data)
|
||||
};
|
||||
match request_status {
|
||||
Ok(true) => {
|
||||
if socket.can_send() {
|
||||
pages::serve(socket, &request);
|
||||
}
|
||||
request.reset();
|
||||
socket.close();
|
||||
}
|
||||
Ok(false) => (),
|
||||
Err(err) => {
|
||||
println!("failed HTTP request: {}", err);
|
||||
request.reset();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
} else if socket.may_send() {
|
||||
request.reset();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
let timestamp_ms = 0; // TODO
|
||||
match iface.poll(&mut sockets, timestamp_ms) {
|
||||
Ok(()) | Err(Error::Exhausted) => (),
|
||||
Err(e) => println!("poll error: {}", e)
|
||||
}
|
||||
|
||||
if time > next_blink {
|
||||
led_state = !led_state;
|
||||
next_blink = time + 1000;
|
||||
|
@ -170,6 +275,7 @@ Ready."#);
|
|||
next_info = next_info + 3000;
|
||||
}
|
||||
|
||||
board::process_errors();
|
||||
if board::error_latched() {
|
||||
match latch_reset_time {
|
||||
None => {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
use core::fmt::Write;
|
||||
use smoltcp::socket::TcpSocket;
|
||||
use http;
|
||||
|
||||
pub fn serve(output: &mut TcpSocket, request: &http::Request) {
|
||||
match request.get_path().unwrap() {
|
||||
b"/" => {
|
||||
let data = include_str!("index.html");
|
||||
http::write_reply_header(output, 200, "text/html; charset=utf-8", false).unwrap();
|
||||
output.write_str(data).unwrap();
|
||||
},
|
||||
b"/style.css" => {
|
||||
let data = include_bytes!("style.css.gz");
|
||||
http::write_reply_header(output, 200, "text/css", true).unwrap();
|
||||
output.send_slice(data).unwrap();
|
||||
},
|
||||
b"/logo.svg" => {
|
||||
let data = include_bytes!("logo.svg.gz");
|
||||
http::write_reply_header(output, 200, "image/svg+xml", true).unwrap();
|
||||
output.send_slice(data).unwrap();
|
||||
},
|
||||
_ => {
|
||||
http::write_reply_header(output, 404, "text/plain", false).unwrap();
|
||||
write!(output, "Not found").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue