diff --git a/.vscode/launch.json b/.vscode/launch.json index 31e7df83762744c2c32c5dc5ff824e1e5d8a4fda..11891954316b6c34d718bd10c6437f1a38fa4e1f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,28 +1,33 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "gdb", - "request": "attach", - "name": "Debug embedded", - "usewsl": true, - "gdbpath": "arm-none-eabi-gdb", - "executable": "./target/thumbv7em-none-eabihf/debug/d7018e_coap", - "target": "192.168.1.4:3333", - "remote": true, - "autorun": [ - "monitor reset init", - "monitor arm semihosting enable", - "monitor tpiu config internal /tmp/itm.log uart off 84000000", - "monitor itm port 0 on", - "load", - "monitor reset init", - "continue" - ], - "cwd": "${workspaceRoot}" - }, - ] +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "gdb", + "request": "launch", + "target": "./bin/executable", + "cwd": "${workspaceRoot}" + }, + { + "type": "gdb", + "request": "attach", + "name": "Debug embedded", + "gdbpath": "/usr/bin/arm-none-eabi-gdb", + "executable": "./target/thumbv7em-none-eabihf/debug/app", + "target": "192.168.1.4:3333", + "remote": true, + "autorun": [ + "monitor reset init", + "monitor arm semihosting enable", + "monitor tpiu config internal itm.log uart off 64000000", + "monitor itm port 0 on", + "load", + "monitor reset init" + ], + "cwd": "${workspaceRoot}" + }, + ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index bd0ae4f2161c9e1ce87a78cebf6b3d215aaedcb6..f15fdf04ab47c2ea89a8aad09ca6500f43f8aa03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,65 +1,20 @@ [package] -name = "d7018e_coap" +name = "xoap" version = "0.1.0" -authors = ["Joakim Lundberg <joakim@joakimlundberg.com>"] -categories = ["embedded", "hardware-support", "no-std"] -description = "CoAP on embedded rust" +description = "A CoAP library for bare-metal applications" readme = "README.md" -repository = "https://github.com/Shawnshank/D7018E_Project" -keywords = ["CoAP", "Embedded", "arm", "stm32"] +documentation = "" +repository = "" license = "MIT OR Apache-2.0" +authors = ["Joakim Lundberg <joakim@joakimlundberg.com>"] +keywords = ["CoAP", "xoap"] [dependencies] -cortex-m-semihosting = "0.2.0" -static-ref = "0.2.0" -volatile-register = "0.2.0" -m = "0.1.1" -heapless = "0.2.4" -f4 = {git = "https://github.com/jsjolund/f4"} -#xoap = { path = "../projects/xoap", version = "0.1.0", default-features = false, features = ["baremetal"] }# git = "https://github.com/Shawnshank/xoap" - -[dependencies.smoltcp] -git = "https://github.com/m-labs/smoltcp" -rev = "cd893e6a" -default-features = false -features = ["proto-ipv4", "socket-tcp"] - -[dependencies.stm32f40x] -git = "https://gitlab.henriktjader.com/pln/STM32F40x.git" -features = ["rt"] -version = "0.2.0" - -[dependencies.cast] -default-features = false -version = "0.2.2" - -[dependencies.cortex-m] -version = "0.3.1" - -[dependencies.either] -default-features = false -version = "1.3.0" - -[dependencies.embedded-hal] -git = "https://github.com/japaric/embedded-hal" -rev = "7d904f515d15fd5fe7ea34e18820ea83e2651fa2" - -[dependencies.nb] -git = "https://github.com/japaric/nb" - -[dependencies.cortex-m-rt] -features = ["abort-on-panic"] -version = "0.3.8" - -[dependencies.cortex-m-debug] -git = "https://gitlab.henriktjader.com/pln/cortex-m-debug.git" - -[dependencies.cortex-m-rtfm] -version = "0.2.0" +# Baremetal dependencies +heapless = {version = "0.2.1"} +#nb = {git = "https://github.com/japaric/nb", optional = true} +cast = {default-features = false, version = "0.2.2", optional = true} -[profile.release] -lto = true +[dev-dependencies] +quickcheck = "0.2.27" -[profile.dev] -incremental = false -codegen-units = 1 \ No newline at end of file diff --git a/README.md b/README.md index 2f7fa8be0dd3a62b87131a8ae61a45e52bb840ce..453de44328ba9aab2cae0a9c6fdbff1eb37fa259 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,22 @@ -# Embedded rust with CoAP -This project aims to make [CoAP](https://tools.ietf.org/html/rfc7252) on ARM embedded systems coded with [Rust](https://www.rust-lang.org) a thing. - -This project is part of the course D7018E at [LuleƄ tekniska universitet](https://www.ltu.se) taught by [Per Lindgren](https://www.ltu.se/staff/p/pln-1.11258). - -## Installation - -## Crates used -- [coap](http://covertness.github.io/coap-rs/coap/index.html) -- [smoltcp](https://crates.io/crates/smoltcp) - - -## ESP8266 -As a WiFi bridge the ESP8266 is used and communicates with the CoAP server/client over USART using [SLIP](https://tools.ietf.org/html/rfc1055) -### Firmware -The firmware used for the esp8266 module is [esp-just-slip](https://github.com/krzychb/esp-just-slip) +# XoAP (NOT WORKING YET) +[](./LICENSE) + +CoAp for embedded systems in Rust. + +Based on [coap](http://covertness.github.io/coap-rs/coap/index.html) by Covertness + +## Installation + +First add this to your `Cargo.toml`: + +```toml +[dependencies.xoap] +version = "0.1.0" +git = "https://github.com/Shawnshank/xoap" +``` + +Then, add this to your crate root: + +```rust +extern crate xoap; +``` diff --git a/src/header.rs b/src/header.rs new file mode 100644 index 0000000000000000000000000000000000000000..5c219034d97475c5b4ded1bca909554212d5a8df --- /dev/null +++ b/src/header.rs @@ -0,0 +1,161 @@ +/// Defines the header for a CoAP message + +pub const MAX_OPTIONS: usize = 10; + +/* Fixed 4 bytes (Sent/Recived header) */ +pub struct HeaderRaw { + ver_type_tkl: u8; + code: u8, + ID: u16, +} + +pub struct Header { + version: const u8 = 1, + type: MessageType, + tkl: u8, + code: u8, + ID: u16, +} + +// Only use piggyback responses +pub struct Token { + value: u8, +} + +pub enum MessageType { + None = -1, + Confirmable = 0, + NonConfirmable = 1, + Acknowledgement = 2, + Reset = 3, +} + +impl HeaderRaw { + pub fn new() -> Self { + HeaderRaw { + ver_type_tkl: 64, // version = 1 the rest is 0 + code: 0, + ID: 0, + } + } + + pub fn decode_raw(&self) -> Header { + let newHeader = Header::new(); + newHeader.set_tkl(self.ver_type_tkl & 0x0F); + newHeader.set_type(number_to_type((self.ver_type_tkl >> 4) & 0x02); + newHeader.set_code(self.code); + newHeader.set_ID(self.ID); + + return newHeader; + } +} + +impl Header{ + pub fn new() -> Self { + Header { + //version: 1, + type: MessageType::None, + tkl: 0, + code: 0, + ID: 0, + } + } + + pub fn build_raw(&self) -> HeaderRaw { + let newHeader = HeaderRaw::new(); + newHeader.ver_type_tkl = (1 << 6) | self.type_to_number(get_type()) << 4 | self.get_tkl(); + newHeader.code = self.get_code(); + newHeader.ID = self.get_ID(); + + return newHeader; + } + + #[inline] + pub fn get_version(&self) -> u8 { + self.version + } + + #[inline] + pub fn get_type(&self) -> MessageType { + self.type + } + + #[inline] + pub fn set_type(&mut self, t: MessageType) { + self.type = t; + } + + #[inline] + pub fn get_tkl(&self) -> u8 { + self.tkl + } + + #[inline] + pub fn set_tkl(&mut self, t: u8) { + self.tkl = t; + } + + #[inline] + pub fn get_code(&self) -> u8 { + self.code + } + + #[inline] + pub fn set_code(&mut self, c: u8) { + self.code = c; + } + + #[inline] + pub fn get_ID(&self) -> u16 { + self.type + } + + #[inline] + pub fn set_ID(&mut self, id: u16) { + self.ID = id; + } + + #[inline] + pub fn get_classcode(&self) -> u8 { + self.code >> 5 + } + + #[inline] + pub fn set_classcode(&mut self, c: u8) { + let detailcode = self.code & 0x1F; + self.code = (c<<5) | detailcode; + } + + #[inline] + pub fn get_detailcode(&self) -> u8 { + self.code & 0x1F + } + + #[inline] + pub fn set_detailcode(&mut self, c: u8) { + let classcode = self.code & 0xE0; + self.code = c | classcode; + } + + #[inline] + pub fn type_to_number(t: MessageType) -> u8 { + let num = match t => { + MessageType::Confirmable => 0, + MessageType::NonConfirmable => 1, + MessageType::Acknowledgement => 2, + MessageType::Reset => 3, + _ => -1, + } + } + + #[inline] + pub fn number_to_type(n: u8) { + let type = match n { + 0 => MessageType::Confirmable, + 1 => MessageType::NonConfirmable, + 2 => MessageType::Acknowledgement, + 3 => MessageType::Reset, + _ => MessageType::None, + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..b4b89b47df86f14414d41089a9782df71f8cdb75 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +// Xoap lib CoAP for ARM microcontrollers + +#![no_std] +#![warn(unreachable_patterns)] + +extern crate heapless; + +pub mod header; +pub mod options; \ No newline at end of file diff --git a/src/options.rs b/src/options.rs new file mode 100644 index 0000000000000000000000000000000000000000..7c5355109bbc83b1b599ae52e9a01bec7db2ea0e --- /dev/null +++ b/src/options.rs @@ -0,0 +1,152 @@ +use heapless::Vec; +use heapless::String; + +pub const MAX_VALUE_LENGTH: usize = 255; +pub const MAX_OPTIONS: usize = 10; + +/* ++-----+---+---+---+---+----------------+--------+--------+-------------+ +| No. | C | U | N | R | Name | Format | Length | Default | ++-----+---+---+---+---+----------------+--------+--------+-------------+ +| 1 | x | | | x | If-Match | opaque | 0-8 | (none) | +| 3 | x | x | - | | Uri-Host | string | 1-255 | (see note 1)| +| 4 | | | | x | ETag | opaque | 1-8 | (none) | +| 5 | x | | | | If-None-Match | empty | 0 | (none) | +| 7 | x | x | - | | Uri-Port | uint | 0-2 | (see note 1)| +| 8 | | | | x | Location-Path | string | 0-255 | (none) | +| 11 | x | x | - | x | Uri-Path | string | 0-255 | (none) | +| 12 | | | | | Content-Format | uint | 0-2 | (none) | +| 14 | | x | - | | Max-Age | uint | 0-4 | 60 | +| 15 | x | x | - | x | Uri-Query | string | 0-255 | (none) | +| 17 | x | | | | Accept | uint | 0-2 | (none) | +| 20 | | | | x | Location-Query | string | 0-255 | (none) | +| 28 | | | x | | Size2 | uint | 0-4 | (none) | +| 35 | x | x | - | | Proxy-Uri | string | 1-1034 | (none) | +| 39 | x | x | - | | Proxy-Scheme | string | 1-255 | (none) | +| 60 | | | x | | Size1 | uint | 0-4 | (none) | ++-----+---+---+---+---+----------------+--------+--------+-------------+ +C = Critical, U = Unsafe, N = No-cache-Key, R = Repeatable, +*/ + +pub enum ValueFormat { + empty = 0, // A zero-length sequence of bytes. + opaque = 1, // An opaque sequence of bytes. + uint, = 2, // A non-negative integer that is represented in network byte order using the number of bytes given by the Option Length field. + string, = 3, // A Unicode string that is encoded using UTF-8 [RFC3629] in Net-Unicode form [RFC5198]. + block, = 4, +} + +pub struct Format { + delta: u8, // u4 + length: u8, // u4 + value: u8, // TODO +} + +impl Format { + pub fn new() -> Self { + Format { + delta: 0, + length: 0, + value: 0, // TODO // Network byte order - Big-endian + } + } +} + +pub enum Options { + None = 0, // Internal + IfMatch = 1, + URIHost = 3, + ETag = 4, + IfNoneMatch = 5, + URIPort = 7, + LocationPath = 8, + URIPath = 11, + ContentFormat = 12, + MaxAge = 14, + URIQuery = 15, + Accept = 17, + LocationQuery = 20, + Size2 = 28, + ProxyURI = 35, + ProxyScheme = 39, + Size1 = 60, +} + +pub struct OptionHeader { + number: u8, + name: u8, + type: u8, + minLen: u8, + maxLen: u8, + multipleValues: u8, + defaultValue: u8, +} + +impl OptionHeader { + pub fn new() +} + +pub struct CoAPOption { + opt: Options, + format: Format, + length: u8, +} + +impl CoAPOption { + pub fn new() -> Self { + CoAPOption { + opt: Options::None, + format: Format::new(), + length: 0, + } + } + + pub fn +} + +pub fn option_to_number(opt: Options) -> u8 { + let num = match opt { + Options::None => 0, + Options::IfMatch => 1, + Options::URIHost => 3, + Options::ETag => 4, + Options::IfNoneMatch => 5, + Options::URIPort => 7, + Options::LocationPath => 8, + Options::URIPath => 11, + Options::ContentFormat => 12, + Options::MaxAge => 14, + Options::URIQuery => 15, + Options::Accept => 17, + Options::LocationQuery => 20, + Options::Size2 => 28, + Options::ProxyURI => 35, + Options::ProxyScheme => 39, + Options::Size1 => 60, + _ => (), + } + return num; +} + +pub fn number_to_option(num: u8) -> Options { + let opt = match num { + 1 => Options::IfMatch, + 3 => Options::URIHost, + 4 => Options::ETag, + 5 => Options::IfNoneMatch, + 7 => Options::URIPort, + 8 => Options::LocationPath, + 11 => Options::URIPath, + 12 => Options::ContentFormat, + 14 => Options::MaxAge, + 15 => Options::URIQuery, + 17 => Options::Accept, + 20 => Options::LocationQuery, + 28 => Options::Size2, + 35 => Options::ProxyURI, + 39 => Options::ProxyScheme , + 60 => Options::Size1, + _ => (), + } + return opt; +} \ No newline at end of file diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000000000000000000000000000000000000..a10c79c5e0643294d9897040bfd322288968e4d7 --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,14 @@ +use heapless::Vec; + +use header; +use options; + +const MAX_PACKET_SIZE: usize = 255; // Maximum amount of bytes in a singel packet + +pub let PacketRaw: Vec<u8, [u8; MAX_PACKET_SIZE]> = Vec::new(); + +pub struct Packet { + header: header::HeaderRaw, + token: u32, // Should be 8 bytes + options: Vec<options::CoAPOption, [options::CoAPOption; options::MAX_OPTIONS] +} \ No newline at end of file