diff --git a/glue/rust/Cargo.toml b/glue/rust/Cargo.toml index 83a9add..d115754 100644 --- a/glue/rust/Cargo.toml +++ b/glue/rust/Cargo.toml @@ -15,9 +15,10 @@ keywords = ["warduino", "wasm"] license = "MPL-2.0" [dependencies] -num = "0.4" -num-traits = "0.2" -num-derive = "0.3" +num = { version = "0.4.3", default-features = false } +num-derive = { version = "0.4.2", default-features = false } +num-traits = { version = "0.2.19", default-features = false } + [[example]] name = "blink" diff --git a/glue/rust/examples/button.rs b/glue/rust/examples/button.rs index 98c0f2e..084bb2d 100644 --- a/glue/rust/examples/button.rs +++ b/glue/rust/examples/button.rs @@ -1,13 +1,21 @@ // config::BUTTON demo app -use warduino::{delay, digital_read, digital_write, InterruptMode, pin_mode, PinMode, PinVoltage, sub_interrupt}; +use warduino::{ + delay, digital_read, digital_write, pin_mode, sub_interrupt, InterruptMode, PinMode, PinVoltage, +}; mod config; -fn callback(_topic: &str, _payload: &str, _length: u32) { +fn callback( + _topic: *const u8, + _topic_length: usize, + _payload: *const u8, + _payload_length: usize, + _length: u32, +) { let voltage = digital_read(config::LED); match voltage { PinVoltage::HIGH => digital_write(config::LED, PinVoltage::LOW), - PinVoltage::LOW => digital_write(config::LED, PinVoltage::HIGH) + PinVoltage::LOW => digital_write(config::LED, PinVoltage::HIGH), } } @@ -22,4 +30,3 @@ pub fn main() { delay(1000); } } - diff --git a/glue/rust/examples/config.rs b/glue/rust/examples/config.rs index bc89a73..20f3ba0 100644 --- a/glue/rust/examples/config.rs +++ b/glue/rust/examples/config.rs @@ -4,4 +4,6 @@ pub static SSID: &str = "local-network"; pub static PASSWORD: &str = "network-password"; pub static BROKER_URL: &str = "192.168.0.24"; pub static BROKER_PORT: u32 = 1883; -pub static CLIENT_ID: &str = "random-mqtt-client-id"; \ No newline at end of file +pub static CLIENT_ID: &str = "random-mqtt-client-id"; + +fn main() {} diff --git a/glue/rust/examples/smartlamp.rs b/glue/rust/examples/smartlamp.rs index 5825e26..bf6a81e 100644 --- a/glue/rust/examples/smartlamp.rs +++ b/glue/rust/examples/smartlamp.rs @@ -1,7 +1,9 @@ // Simple smart lamp app demo -use warduino::{delay, digital_read, digital_write, InterruptMode, mqtt_connect, mqtt_connected, - mqtt_init, mqtt_loop, mqtt_publish, mqtt_subscribe, pin_mode, PinMode, PinVoltage, - print, sleep, sub_interrupt, wifi_connect, wifi_connected, wifi_localip}; +use warduino::{ + delay, digital_read, digital_write, mqtt_connect, mqtt_connected, mqtt_init, mqtt_loop, + mqtt_publish, mqtt_subscribe, pin_mode, print, sleep, sub_interrupt, wifi_connect, + wifi_connected, wifi_localip, InterruptMode, PinMode, PinVoltage, +}; mod config; @@ -12,13 +14,24 @@ fn until(done: fn() -> bool, attempt: fn()) { } } -fn callback(topic: &str, payload: &str, size: u32) { +fn callback( + topic: *const u8, + topic_len: usize, + payload: *const u8, + payload_length: usize, + size: u32, +) { + let topic = + unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(topic, topic_len)) }; + let payload = unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts(payload, payload_length)) + }; print(&format!("Message [{}] {}\n", topic, payload)); if payload.contains("on") { - digital_write(config::LED, PinVoltage::HIGH); // Turn the LED on + digital_write(config::LED, PinVoltage::HIGH); // Turn the LED on } else { - digital_write(config::LED, PinVoltage::LOW); // Turn the LED off + digital_write(config::LED, PinVoltage::LOW); // Turn the LED off } } @@ -29,7 +42,13 @@ fn invert(voltage: PinVoltage) -> PinVoltage { } } -fn toggle_led(_topic: &str, _payload: &str, _size: u32) { +fn toggle_led( + _topic: *const u8, + _topic_length: usize, + _payload: *const u8, + _payload_length: usize, + _size: u32, +) { // Get current status of LED let status = digital_read(config::LED); // Toggle LED @@ -43,7 +62,9 @@ pub fn main() { pin_mode(config::BUTTON, PinMode::INPUT); // Connect to Wi-Fi - until(wifi_connected, || wifi_connect(config::SSID, config::PASSWORD)); + until(wifi_connected, || { + wifi_connect(config::SSID, config::PASSWORD) + }); let message: String = "Connected to wifi network with ip: ".to_owned() + &wifi_localip(); print(&message); diff --git a/glue/rust/src/lib.rs b/glue/rust/src/lib.rs index fed51b2..845e9e5 100644 --- a/glue/rust/src/lib.rs +++ b/glue/rust/src/lib.rs @@ -34,13 +34,16 @@ //! ``` #![crate_name = "warduino"] +#![no_std] +extern crate alloc; extern crate num; #[macro_use] extern crate num_derive; +use alloc::{borrow::ToOwned, string::String}; +use core::mem; use linking::*; -use std::mem; mod linking; @@ -91,28 +94,46 @@ pub enum PinMode { } /// Returns the number of milliseconds passed since the current program started to run. -pub fn millis() -> u32 { unsafe { _millis() } } +pub fn millis() -> u32 { + unsafe { _millis() } +} /// Pauses the program for the amount of time (in milliseconds). -pub fn delay(ms: u32) { unsafe { _delay(ms); } } +pub fn delay(ms: u32) { + unsafe { + _delay(ms); + } +} /// Pauses the program for the amount of time (in seconds) -pub fn sleep(s: u32) { delay(s * 1000); } +pub fn sleep(s: u32) { + delay(s * 1000); +} /// Configures the [PinMode] of the specified pin. -pub fn pin_mode(pin: u32, mode: PinMode) { unsafe { _pinMode(pin, mode as u32) } } +pub fn pin_mode(pin: u32, mode: PinMode) { + unsafe { _pinMode(pin, mode as u32) } +} /// Write the voltage to a specified digital pin, either [HIGH](PinVoltage) or [LOW](PinVoltage). -pub fn digital_write(pin: u32, value: PinVoltage) { unsafe { _digitalWrite(pin, value as u32) } } +pub fn digital_write(pin: u32, value: PinVoltage) { + unsafe { _digitalWrite(pin, value as u32) } +} /// Reads the value from a specified digital pin, either [HIGH](PinVoltage) or [LOW](PinVoltage). -pub fn digital_read(pin: u32) -> PinVoltage { unsafe { num::FromPrimitive::from_u32(_digitalRead(pin)).unwrap() } } +pub fn digital_read(pin: u32) -> PinVoltage { + unsafe { num::FromPrimitive::from_u32(_digitalRead(pin)).unwrap() } +} /// Reads the value from the specified analog pin. -pub fn analog_read(pin: u32) -> i32 { unsafe { _analogRead(pin) } } +pub fn analog_read(pin: u32) -> i32 { + unsafe { _analogRead(pin) } +} /// Writes the value to the specified analog pin. -pub fn analog_write(pin: u32, signal: u32) -> i32 { unsafe { _analogWrite(pin, signal) } } +pub fn analog_write(pin: u32, signal: u32) -> i32 { + unsafe { _analogWrite(pin, signal) } +} /// The status of the Wi-Fi connection #[derive(FromPrimitive, PartialEq)] @@ -136,59 +157,91 @@ pub enum WiFiStatus { } /// Connect to Wi-Fi network with SSID and password -pub fn wifi_connect(ssid: &str, password: &str) { unsafe { _connect(ssid, password) } } +pub fn wifi_connect(ssid: &str, password: &str) { + unsafe { _connect(ssid.as_ptr(), ssid.len(), password.as_ptr(), password.len()) } +} /// Returns the status of the Wi-Fi connection of the board -pub fn wifi_status() -> WiFiStatus { unsafe { num::FromPrimitive::from_i32(_status()).unwrap() } } +pub fn wifi_status() -> WiFiStatus { + unsafe { num::FromPrimitive::from_i32(_status()).unwrap() } +} /// Returns whether the board si still connected to Wi-Fi -pub fn wifi_connected() -> bool { wifi_status() == WiFiStatus::Connected } +pub fn wifi_connected() -> bool { + wifi_status() == WiFiStatus::Connected +} /// Returns the local IP address of the board -pub fn wifi_localip() -> String { +pub fn with_wifi_localip(then: impl FnOnce(&str) -> T) -> T { unsafe { let buffer: [u8; 100] = [0; 100]; - _localip(buffer.as_ptr(), mem::size_of_val(&buffer) / mem::size_of::()); - std::str::from_utf8(&buffer).unwrap().to_owned() + _localip( + buffer.as_ptr(), + mem::size_of_val(&buffer) / mem::size_of::(), + ); + then(core::str::from_utf8(&buffer).unwrap()) } } const BUFFER_SIZE: usize = 250; /// Send an HTTP GET request. -pub fn get(url: &str) -> &str { - const BUFFER: &[u8; BUFFER_SIZE] = &[0; BUFFER_SIZE]; - unsafe { - _get(url.as_ptr(), url.len(), BUFFER.as_ptr(), mem::size_of_val(BUFFER) / mem::size_of::()); - std::str::from_utf8_unchecked(BUFFER) +pub fn with_get(url: &str, then: impl FnOnce(&str) -> T) -> T { + let BUFFER: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + unsafe { + _get( + url.as_ptr(), + url.len(), + BUFFER.as_ptr(), + mem::size_of_val(&BUFFER) / mem::size_of::(), + ); + then(core::str::from_utf8_unchecked(&BUFFER)) } } /// Send an HTTP POST request. -pub fn post(options: &PostOptions) -> &str { - const BUFFER: &[u8; BUFFER_SIZE] = &[0; BUFFER_SIZE]; +pub fn with_post(options: &PostOptions, then: impl FnOnce(&str) -> T) -> T { + let BUFFER: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; unsafe { - _post(options.uri.as_ptr(), options.uri.len(), - options.body.as_ptr(), options.body.len(), - options.headers.content_type.as_ptr(), options.headers.content_type.len(), - options.headers.authorization.as_ptr(), options.headers.authorization.len(), - BUFFER.as_ptr(), mem::size_of_val(BUFFER) / mem::size_of::()); - std::str::from_utf8_unchecked(BUFFER) + _post( + options.uri.as_ptr(), + options.uri.len(), + options.body.as_ptr(), + options.body.len(), + options.headers.content_type.as_ptr(), + options.headers.content_type.len(), + options.headers.authorization.as_ptr(), + options.headers.authorization.len(), + BUFFER.as_ptr(), + mem::size_of_val(&BUFFER) / mem::size_of::(), + ); + then(core::str::from_utf8_unchecked(&BUFFER)) } - } /// Print a string to the serial port. -pub fn print(text: &str) { unsafe { _print_buffer(text.as_ptr(), text.len()) } } +pub fn print(text: &str) { + unsafe { _print_buffer(text.as_ptr(), text.len()) } +} /// Print an integer to the serial port. -pub fn print_int(integer: i32) { unsafe { _print_int(integer) } } +pub fn print_int(integer: i32) { + unsafe { _print_int(integer) } +} /// subscribe a callback function to an interrupt on the given pin -pub fn sub_interrupt(pin: u32, mode: InterruptMode, f: fn(&str, &str, u32)) { unsafe { _sub_interrupt(pin, f, mode as u32) } } +pub fn sub_interrupt( + pin: u32, + mode: InterruptMode, + f: fn(*const u8, usize, *const u8, usize, u32), +) { + unsafe { _sub_interrupt(pin, f, mode as u32) } +} /// Unsubscribe all callback functions for a given pin -pub fn unsub_interrupt(pin: u32) { unsafe { _unsub_interrupt(pin) } } +pub fn unsub_interrupt(pin: u32) { + unsafe { _unsub_interrupt(pin) } +} /// The status of the MQTT connection #[derive(FromPrimitive, PartialEq)] @@ -216,26 +269,41 @@ pub enum MQTTStatus { } /// Configure a MQTT broker -pub fn mqtt_init(server: &str, port: u32) { unsafe { _mqtt_init(server.as_ptr(), server.len(), port) } } +pub fn mqtt_init(server: &str, port: u32) { + unsafe { _mqtt_init(server.as_ptr(), server.len(), port) } +} /// Connect to the Configured MQTT broker with client_id -pub fn mqtt_connect(client_id: &str) -> bool { unsafe { _mqtt_connect(client_id.as_ptr(), client_id.len()) != 0 } } +pub fn mqtt_connect(client_id: &str) -> bool { + unsafe { _mqtt_connect(client_id.as_ptr(), client_id.len()) != 0 } +} /// Returns whether the board is still connected to the MQTT broker -pub fn mqtt_connected() -> bool { mqtt_state() == MQTTStatus::Connected } +pub fn mqtt_connected() -> bool { + mqtt_state() == MQTTStatus::Connected +} /// Returns the status of the connection to the MQTT broker -pub fn mqtt_state() -> MQTTStatus { unsafe { num::FromPrimitive::from_i32(_mqtt_state()).unwrap() } } +pub fn mqtt_state() -> MQTTStatus { + unsafe { num::FromPrimitive::from_i32(_mqtt_state()).unwrap() } +} /// Publish a message on an MQTT topic -pub fn mqtt_publish(topic: &str, payload: &str) -> i32 { unsafe { _mqtt_publish(topic.as_ptr(), topic.len(), payload.as_ptr(), payload.len()) } } +pub fn mqtt_publish(topic: &str, payload: &str) -> i32 { + unsafe { _mqtt_publish(topic.as_ptr(), topic.len(), payload.as_ptr(), payload.len()) } +} /// Subscribe a callback function to an MQTT topic -pub fn mqtt_subscribe(topic: &str, f: fn(&str, &str, u32)) -> i32 { unsafe { _mqtt_subscribe(topic.as_ptr(), topic.len(), f) } } +pub fn mqtt_subscribe(topic: &str, f: fn(*const u8, usize, *const u8, usize, u32)) -> i32 { + unsafe { _mqtt_subscribe(topic.as_ptr(), topic.len(), f) } +} /// Unsubscribe a callback function from an MQTT topic -pub fn mqtt_unsubscribe(topic: &str, f: fn(&str, &str, u32)) -> i32 { unsafe { _mqtt_unsubscribe(topic.as_ptr(), topic.len(), f) } } +pub fn mqtt_unsubscribe(topic: &str, f: fn(*const u8, usize, *const u8, usize, u32)) -> i32 { + unsafe { _mqtt_unsubscribe(topic.as_ptr(), topic.len(), f) } +} /// Check for messages from the MQTT broker -pub fn mqtt_poll() -> i32 { unsafe { _mqtt_loop() } } - +pub fn mqtt_poll() -> i32 { + unsafe { _mqtt_loop() } +} diff --git a/glue/rust/src/linking.rs b/glue/rust/src/linking.rs index 475ab28..07e0520 100644 --- a/glue/rust/src/linking.rs +++ b/glue/rust/src/linking.rs @@ -1,5 +1,5 @@ #[link(wasm_import_module = "env")] -extern { +extern "C" { // Time #[link_name = "millis"] pub fn _millis() -> u32; @@ -28,7 +28,12 @@ extern { // Wi-Fi #[link_name = "wifi_connect"] - pub fn _connect(ssid: &str, password: &str); + pub fn _connect( + ssid: *const u8, + ssid_length: usize, + password: *const u8, + password_length: usize, + ); #[link_name = "wifi_status"] pub fn _status() -> i32; #[link_name = "wifi_localip"] @@ -38,16 +43,22 @@ extern { #[link_name = "http_get"] pub fn _get(url: *const u8, url_len: usize, buffer: *const u8, buffer_size: usize) -> i32; #[link_name = "http_post"] - pub fn _post(url: *const u8, url_len: usize, - body: *const u8, body_len: usize, - content_type: *const u8, content_type_len: usize, - authorization: *const u8, authorization_len: usize, - buffer: *const u8, buffer_size: usize) - -> i32; + pub fn _post( + url: *const u8, + url_len: usize, + body: *const u8, + body_len: usize, + content_type: *const u8, + content_type_len: usize, + authorization: *const u8, + authorization_len: usize, + buffer: *const u8, + buffer_size: usize, + ) -> i32; // Interrupts #[link_name = "subscribe_interrupt"] - pub fn _sub_interrupt(pin: u32, f: fn(&str, &str, u32), mode: u32); + pub fn _sub_interrupt(pin: u32, f: fn(*const u8, usize, *const u8, usize, u32), mode: u32); #[link_name = "unsubscribe_interrupt"] pub fn _unsub_interrupt(pin: u32); @@ -61,11 +72,24 @@ extern { #[link_name = "mqtt_state"] pub fn _mqtt_state() -> i32; #[link_name = "mqtt_publish"] - pub fn _mqtt_publish(topic: *const u8, topic_length: usize, payload: *const u8, payload_length: usize) -> i32; + pub fn _mqtt_publish( + topic: *const u8, + topic_length: usize, + payload: *const u8, + payload_length: usize, + ) -> i32; #[link_name = "mqtt_subscribe"] - pub fn _mqtt_subscribe(topic: *const u8, topic_length: usize, f: fn(&str, &str, u32)) -> i32; + pub fn _mqtt_subscribe( + topic: *const u8, + topic_length: usize, + f: fn(*const u8, usize, *const u8, usize, u32), + ) -> i32; #[link_name = "mqtt_unsubscribe"] - pub fn _mqtt_unsubscribe(topic: *const u8, topic_length: usize, f: fn(&str, &str, u32)) -> i32; + pub fn _mqtt_unsubscribe( + topic: *const u8, + topic_length: usize, + f: fn(*const u8, usize, *const u8, usize, u32), + ) -> i32; #[link_name = "mqtt_loop"] pub fn _mqtt_loop() -> i32; }