diff --git a/.gitignore b/.gitignore index 83d0832..b401aad 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target openocd.log firmware.map Cargo.lock +src/config.rs diff --git a/Cargo.toml b/Cargo.toml index c955d47..11a530a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,9 @@ authors = ["Dave Hylands "] cc3200 = { path = "cc3200-rs" } freertos_alloc = { path = "cc3200-rs/freertos_alloc" } freertos_rs = "0.1" +log = { version = "0.3", default-features = false } sensorweb-sys = { path = "sensorweb-sys" } +smallhttp = { git = "https://github.com/fabricedesre/smallhttp.git" } [profile.dev] panic = "abort" diff --git a/cc3200-rs b/cc3200-rs index 3ea54dd..ae14c41 160000 --- a/cc3200-rs +++ b/cc3200-rs @@ -1 +1 @@ -Subproject commit 3ea54dd9037d9d7d8c1105f1e70c0f13e7484fef +Subproject commit ae14c416534c3d3074563e0273f6076873f8bb44 diff --git a/scripts/travis.sh b/scripts/travis.sh index 837713c..2390769 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -1,13 +1,17 @@ #!/bin/sh -arm-none-eabi-gcc --version - curl https://sh.rustup.rs -sSf -o rustup.sh chmod +x ./rustup.sh ./rustup.sh -y export PATH=/home/travis/.cargo/bin:$PATH -rustup default nightly +rustup override set nightly-2016-11-06 rustup component add rust-src cargo install xargo +cargo --version +xargo --version +rustc --version +arm-none-eabi-gcc --version + +cp src/config.rs.sample src/config.rs ./build.sh ./build.sh --release diff --git a/src/config.rs.sample b/src/config.rs.sample new file mode 100644 index 0000000..619e5db --- /dev/null +++ b/src/config.rs.sample @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +use cc3200::simplelink::SlSecParams; + +// The default SSID you want to connect to. +pub const SSID: &'static str = "OpenWireless.org"; + +pub fn security_params() -> Option { + // If using an open access point, just return None + Some(SlSecParams::wpa2("YOUR-PASSWORD-HERE")) + //None +} + +pub const SENSOR_READING_COUNT: u32 = 10; +pub const SERVER_URL: &'static str = "http://10.252.33.211:8000/endpoint"; diff --git a/src/main.rs b/src/main.rs index 12f37f4..579e039 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,101 @@ // those are not available in this platform. #![no_std] #![feature(alloc)] +#![feature(collections)] +#[macro_use] extern crate cc3200; extern crate alloc; extern crate freertos_rs; extern crate freertos_alloc; -extern crate sensorweb_sys; -use cc3200::cc3200::{Board, Console, Utils, LedEnum, LedName}; +extern crate smallhttp; -use alloc::arc::Arc; -use freertos_rs::{CurrentTask, Duration, Task, Queue}; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate collections; + +use cc3200::cc3200::{Board, I2C, I2COpenMode, LedEnum}; +use cc3200::format::*; +use cc3200::simplelink::{self, SimpleLink}; +use cc3200::socket_channel::SocketChannel; + +use cc3200::i2c_devices::TemperatureSensor; +use cc3200::tmp006::TMP006; + +use core::str; + +use freertos_rs::{CurrentTask, Duration, Task}; +use smallhttp::Client; +use smallhttp::traits::Channel; + +static VERSION: &'static str = "1.0"; + +mod config; +mod wlan; + +fn buf_find(buf: &[u8], needle: &str) -> Option { + if let Ok(s) = str::from_utf8(buf) { + s.find(needle) + } else { + None + } +} + +fn run() -> Result<(), wlan::Error> { + + Board::led_configure(&[LedEnum::LED1]); + + try!(SimpleLink::start_spawn_task()); + try!(wlan::wlan_station_mode()); + + let i2c = I2C::open(I2COpenMode::MasterModeFst).unwrap(); + let temp_sensor = TMP006::default(&i2c).unwrap(); + + println!("Will now send {} temperature sensing to the server...", + config::SENSOR_READING_COUNT); + + for _ in 0..config::SENSOR_READING_COUNT { + let temperature = temp_sensor.get_temperature().unwrap(); + + // Format a simple json payload that we'll POST to the server + let mut buf: [u8; 24] = [b' '; 24]; + let json = b"{ \"temperature\": @@@@@ }"; + buf[0..json.len()].copy_from_slice(json); + let num_tmpl = "@@@@@"; + let num_idx = buf_find(&buf, num_tmpl).unwrap(); + if format_float_into(&mut buf[num_idx..num_idx + num_tmpl.len()], + temperature, + 1 /* digit after decimal */) { + info!("Feels like {} C", + str::from_utf8(&buf[num_idx..num_idx + num_tmpl.len()]).unwrap()); + info!("Sending {}", str::from_utf8(&buf).unwrap()); + + let mut client = Client::new(SocketChannel::new().unwrap()); + let response = client.post(config::SERVER_URL) + .open() + .unwrap() + .send(json) + .unwrap() + .response(|_| false) // Not interested in any header. + .unwrap(); + let mut buffer = [0u8; 256]; + info!("Received {}", + response.body.read_string_to_end(&mut buffer).unwrap()); + } else { + error!("Failed to format temperature float."); + } + + + CurrentTask::delay(Duration::ms(1000)) + } + + // Power off the network processor. + try!(SimpleLink::stop(simplelink::SL_STOP_TIMEOUT)); + Ok(()) +} // Conceptually, this is our program "entry point". It's the first thing the microcontroller will // execute when it (re)boots. (As far as the linker is concerned the entry point must be named @@ -33,76 +117,18 @@ pub fn start() -> ! { Board::init(); - Console::init_term(); - Console::clear_term(); - Console::message("CC3200 Sample code\n"); - - unsafe { - sensorweb_sys::sensorweb_test_func(); - } - - let queue = Arc::new(Queue::new(10).unwrap()); - let _producer = { - let queue = queue.clone(); - Task::new() - .name("producer") - .start(move || { - let msgs = ["Welcome ", "to ", "CC32xx ", "development !\n"]; - loop { - for msg in msgs.iter() { - queue.send(msg, Duration::ms(15)).unwrap(); - CurrentTask::delay(Duration::ms(15)) - } - CurrentTask::delay(Duration::ms(1000)) - } - }) - .unwrap() - }; - - let _consumer = { - let queue = queue.clone(); - Task::new() - .name("consumer") - .start(move || { - loop { - let msg = queue.receive(Duration::ms(2000)).unwrap(); - Console::message("Received: "); - Console::message(msg); - Console::message("\n"); - } - }) - .unwrap() - }; + println!("Welcome to SensorWeb {}", VERSION); - let _blinky = { + let _client = { Task::new() - .name("blinky") + .name("client") + .stack_size(2048) // 32-bit words .start(|| { - Board::led_configure(&[LedEnum::LED1, LedEnum::LED2, LedEnum::LED3]); - Board::led_off(LedName::MCU_ALL_LED_IND); - let mut counter = 0; - loop { - Board::led_on(LedName::MCU_RED_LED_GPIO); - if counter & 1 != 0 { - Board::led_on(LedName::MCU_ORANGE_LED_GPIO); - } else { - Board::led_off(LedName::MCU_ORANGE_LED_GPIO); - } - if counter & 2 != 0 { - Board::led_on(LedName::MCU_GREEN_LED_GPIO); - } else { - Board::led_off(LedName::MCU_GREEN_LED_GPIO); - } - Utils::delay(1333333); - Board::led_off(LedName::MCU_RED_LED_GPIO); - Utils::delay(1333333); - Board::led_on(LedName::MCU_RED_LED_GPIO); - Utils::delay(1333333); - Board::led_off(LedName::MCU_RED_LED_GPIO); - Utils::delay(1333333 * 6); - - counter += 1; - } + match run() { + Ok(()) => { println!("sensorweb succeeded"); }, + Err(e) => { println!("sensorweb failed: {:?}", e); }, + }; + loop {} }) .unwrap() }; diff --git a/src/wlan.rs b/src/wlan.rs new file mode 100644 index 0000000..1ecc3e6 --- /dev/null +++ b/src/wlan.rs @@ -0,0 +1,155 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +use cc3200::cc3200::{Board, LedName}; +use cc3200::simplelink::{self, NetConfigSet, Policy, SimpleLink, SimpleLinkError, WlanConfig, + WlanMode, WlanRxFilterOp, WlanRxFilterOpBuf}; +use freertos_rs::{CurrentTask, Duration}; +use config; + +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub enum AppError { + DEVICE_NOT_IN_STATION_MODE, + PING_FAILED, + INTERNET_CONNECTION_FAILED, + LAN_CONNECTION_FAILED, +} + +#[derive(Debug)] +pub enum Error { + SLE(SimpleLinkError), + App(AppError), +} + +impl From for Error { + fn from(err: SimpleLinkError) -> Error { + Error::SLE(err) + } +} + +impl From for Error { + fn from(err: AppError) -> Error { + Error::App(err) + } +} + +fn configure_simple_link_to_default() -> Result<(), Error> { + let mode = try!(SimpleLink::start()); + if mode != WlanMode::ROLE_STA { + if mode == WlanMode::ROLE_AP { + // If the device is in AP mode, then we need to wait for the + // acquired event before doing anything. + + while !SimpleLink::is_ip_acquired() { + CurrentTask::delay(Duration::ms(100)); + } + } + + // Switch to STA mode and restart + + try!(SimpleLink::wlan_set_mode(WlanMode::ROLE_STA)); + try!(SimpleLink::stop(255)); + let mode = try!(SimpleLink::start()); + if mode != WlanMode::ROLE_STA { + return Err(Error::App(AppError::DEVICE_NOT_IN_STATION_MODE)); + } + } + + // Get the device's version-information + let ver = SimpleLink::get_version(); + + println!("Host Driver Version: {}", SimpleLink::get_driver_version()); + println!("Build Version {}.{}.{}.{}.31.{}.{}.{}.{}.{}.{}.{}.{}", + ver.nwp_version[0], + ver.nwp_version[1], + ver.nwp_version[2], + ver.nwp_version[3], + ver.fw_version[0], + ver.fw_version[1], + ver.fw_version[2], + ver.fw_version[3], + ver.phy_version[0], + ver.phy_version[1], + ver.phy_version[2], + ver.phy_version[3]); + + // Set connection policy to Auto + SmartConfig + // (Device's default connection policy) + try!(SimpleLink::wlan_set_policy(Policy::ConnectionDefault, &[])); + + // Remove all profiles + try!(SimpleLink::wlan_delete_profile(0xff)); + + // Device is in station mode. Disconnect previous connection, if any. + if SimpleLink::wlan_disconnect().is_ok() { + // This means that we were previously connected. Wait for the + // notification event. + while !SimpleLink::is_connected() { + CurrentTask::delay(Duration::ms(100)); + } + } + + // Enable DHCP client + try!(SimpleLink::netcfg_set(NetConfigSet::Ipv4StaP2pClientDhcpEnable, &[1])); + + // Disable Scan + try!(SimpleLink::wlan_set_policy(Policy::ScanDisable, &[])); + + // Set Tx power level for station mode + // Number between 0-15, as dB offset from max power - 0 will set max power + + try!(SimpleLink::wlan_set(WlanConfig::GeneralStaTxPower, &[0])); + + // Set PM policy to normal + try!(SimpleLink::wlan_set_policy(Policy::PowerNormal, &[])); + + // Unregister mDNS services + try!(SimpleLink::netapp_mdns_unregister_service("")); + + // Remove all 64 filters (8*8) + + let all_filters = WlanRxFilterOpBuf::all_filters(); + try!(SimpleLink::wlan_rx_filter(WlanRxFilterOp::Remove, &all_filters)); + + try!(SimpleLink::stop(simplelink::SL_STOP_TIMEOUT)); + + SimpleLink::init_app_variables(); + Ok(()) +} + +fn wlan_connect() -> Result<(), Error> { + + let sec_params = config::security_params(); + + try!(SimpleLink::wlan_connect(config::SSID, &[], sec_params, None)); + + println!("Connecting to {} ...", config::SSID); + // Wait for WLAN event + while !SimpleLink::is_connected() || !SimpleLink::is_ip_acquired() { + // Toggle LEDs to indicate Connection Progress + Board::led_on(LedName::MCU_RED_LED_GPIO); + CurrentTask::delay(Duration::ms(100)); + Board::led_off(LedName::MCU_RED_LED_GPIO); + CurrentTask::delay(Duration::ms(100)); + } + Ok(()) +} + +pub fn wlan_station_mode() -> Result<(), Error> { + SimpleLink::init_app_variables(); + + try!(configure_simple_link_to_default()); + let mode = try!(SimpleLink::start()); + if mode != WlanMode::ROLE_STA { + return Err(Error::App(AppError::DEVICE_NOT_IN_STATION_MODE)); + } + println!("Device started as STATION"); + + try!(wlan_connect()); + + println!("Connection established w/ AP and IP is aquired"); + + Ok(()) +} \ No newline at end of file