From 8b35c4260a0893ce683d4cbde55ee0a72c2f6741 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 1 Dec 2019 17:03:02 +0800 Subject: [PATCH] first tnetplug version --- Cargo.lock | 36 +++++++++--------- Cargo.toml | 15 +++----- README.md | 63 +++++-------------------------- default.nix | 12 +++--- nix/{adc2tcp.nix => tnetplug.nix} | 14 +++---- release.nix | 4 +- shell.nix | 2 +- src/adc_input.rs | 46 ---------------------- src/led.rs | 46 ---------------------- src/main.rs | 42 +++++++-------------- src/net.rs | 2 +- src/server.rs | 36 +++++++++++------- 12 files changed, 84 insertions(+), 234 deletions(-) rename nix/{adc2tcp.nix => tnetplug.nix} (70%) delete mode 100644 src/adc_input.rs delete mode 100644 src/led.rs diff --git a/Cargo.lock b/Cargo.lock index c5221d3..1367ee0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,23 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "adc2tcp" -version = "0.0.0" -dependencies = [ - "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-log 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash2hwaddr 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32-eth 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32f4xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "aligned" version = "0.3.1" @@ -300,6 +282,24 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tnetplug" +version = "0.1.0" +dependencies = [ + "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-log 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash2hwaddr 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32-eth 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32f4xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" diff --git a/Cargo.toml b/Cargo.toml index 39392c4..1d2ae8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,13 @@ [package] categories = ["embedded", "no-std"] -name = "adc2tcp" -description = "Poll ADC pin, report over TCP" +name = "tnetplug" +description = "Trivial network-controlled plugs" license = "GPL-3.0-only" -authors = ["Astro "] -version = "0.0.0" -keywords = ["ethernet", "eth", "stm32", "adc", "tcp"] -repository = "https://github.com/m-labs/adc2tcp" +authors = ["Sebastien Bourdeauducq "] +version = "0.1.0" +keywords = ["ethernet", "eth", "stm32", "smartplug", "internetofshit"] edition = "2018" -[badges] -travis-ci = { repository = "astro/adc2tcp", branch = "master" } -maintenance = { status = "experimental" } - [package.metadata.docs.rs] features = [] default-target = "thumbv7em-none-eabihf" diff --git a/README.md b/README.md index 3b63f18..ec52284 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,21 @@ -# Synopsis - -Exposes readings from an ADC pin (currently: *PA3*) of the board via a -TCP service on the Ethernet port. +Trivial network-controlled plugs +================================ -# Network Protocol - -Sensor readings produce lines of `key=value` pairs, joined by `,`, -terminated by `"\r\n"`. - -``` -t=21000,pa3=685 -t=22000,pa3=684 -t=23000,pa3=681 -t=24000,pa3=696 -t=25000,pa3=673 -t=26000,pa3=689 -t=27000,pa3=657 -t=28000,pa3=654 -t=29000,pa3=652 -t=30000,pa3=662 -t=31000,pa3=663 -``` - -| Key | Value | Unit | -|:---:|-------------|------| -| t | Time | ms | -| pa3 | ADC reading | mV | - - -# LEDs - -Colors indicate what the MCU is occupied with. - -| Color | Indication | -|:-------:|-------------------| -| Green | WFI (idle) | -| Blue | Network poll | -| Red | Message broadcast | - - -# Crate features - -* `semihosting` enables log output via the **cortex-m-semihosting** - crate. Use only in development! MCU will hang when no OpenOCD is - running. - -* `generate-hwaddr` generates an Ethernet MAC address by hashing the - unique device ID from flash memory. - - -# Instructions +Instructions +------------ ![Made for NixOS](https://nixos.org/logo/nixos-lores.png) -## Build the firmware with `default.nix` +Build the firmware with `default.nix` ++++++++++++++++++++++++++++++++++++++ * `nix-build` * This uses **cargo-vendor** to bundle dependencies, so that unstable versions from git can be used. -* Run `result/bin/flash-adc2tcp` to flash a devboard with OpenOCD and quit. +* Run `result/bin/flash-tnetplug` to flash a devboard with OpenOCD and quit. -## Development environment with `shell.nix` +Development environment with `shell.nix` +++++++++++++++++++++++++++++++++++++++++ * `nix-shell` * Spawning `openocd`, the devboard should be connected already. diff --git a/default.nix b/default.nix index 5af048d..0267f38 100644 --- a/default.nix +++ b/default.nix @@ -11,13 +11,13 @@ let rustPlatform = recurseIntoAttrs (callPackage ./nix/rustPlatform.nix { inherit rustManifest; }); - adc2tcp = callPackage ./nix/adc2tcp.nix { inherit rustPlatform; }; + tnetplug = callPackage ./nix/tnetplug.nix { inherit rustPlatform; }; openocd = callPackage ./nix/openocd.nix {}; in stdenv.mkDerivation { - name = "adc2tcp-dist"; + name = "tnetplug-dist"; buildInputs = [ - adc2tcp + tnetplug openocd makeWrapper ]; @@ -26,7 +26,7 @@ stdenv.mkDerivation { installPhase = let - firmwareBinary = "$out/lib/adc2tcp.elf"; + firmwareBinary = "$out/lib/tnetplug.elf"; openOcdFlags = [ "-c" "reset halt" "-c" "flash write_image erase ${firmwareBinary}" @@ -37,9 +37,9 @@ stdenv.mkDerivation { in '' mkdir -p $out/bin $out/lib $out/nix-support - ln -s ${adc2tcp}/lib/adc2tcp ${firmwareBinary} + ln -s ${tnetplug}/lib/tnetplug ${firmwareBinary} - makeWrapper ${openocd}/bin/openocd-nucleo-f429zi $out/bin/flash-adc2tcp \ + makeWrapper ${openocd}/bin/openocd-nucleo-f429zi $out/bin/flash-tnetplug \ --add-flags "${lib.escapeShellArgs openOcdFlags}" echo file binary-dist ${firmwareBinary} >> $out/nix-support/hydra-build-products diff --git a/nix/adc2tcp.nix b/nix/tnetplug.nix similarity index 70% rename from nix/adc2tcp.nix rename to nix/tnetplug.nix index 1eeaf6b..681e379 100644 --- a/nix/adc2tcp.nix +++ b/nix/tnetplug.nix @@ -2,33 +2,33 @@ with rustPlatform; let - sha256 = "1i9p5d5n01ajbp8lmavyway6vr1mmy107qnccff9glvr91rqx352"; + sha256 = "19cdc0lkm9247n6kf23ki66gysz530j1x2lfnzq7n0cpcs53q3h3"; fetchcargo = import ./fetchcargo.nix { inherit stdenv cacert git cargo-vendor; inherit (rust) cargo; }; - adc2tcpDeps = fetchcargo { - name = "adc2tcp"; + tnetplugDeps = fetchcargo { + name = "tnetplug"; src = ../.; inherit sha256; }; in buildRustPackage rec { - name = "adc2tcp"; + name = "tnetplug"; version = "0.0.0"; src = ../.; cargoSha256 = sha256; - buildInputs = [ adc2tcpDeps ]; + buildInputs = [ tnetplugDeps ]; patchPhase = '' cat >> .cargo/config <; }; in { - build = lib.hydraJob adc2tcp; + build = lib.hydraJob tnetplug; } diff --git a/shell.nix b/shell.nix index b6b38a0..0dd45c9 100644 --- a/shell.nix +++ b/shell.nix @@ -10,7 +10,7 @@ let openocd = callPackage ./nix/openocd.nix {}; in stdenv.mkDerivation { - name = "adc2tcp-env"; + name = "tnetplug-env"; buildInputs = with rustPlatform.rust; [ rustc cargo pkgs.gdb ]; diff --git a/src/adc_input.rs b/src/adc_input.rs deleted file mode 100644 index aebf953..0000000 --- a/src/adc_input.rs +++ /dev/null @@ -1,46 +0,0 @@ -use stm32f4xx_hal::{ - adc::{ - Adc, - config::*, - }, - gpio::{Analog, gpioa::PA3 as Pin}, - stm32::ADC1 as ADC, -}; - -/// ADC Input -pub struct AdcInput { - /// unused but consumed - _pin: Pin, - adc: Adc, -} - -impl AdcInput { - /// Configure pin into analog mode - pub fn new(adc: ADC, pin: Pin) -> Self { - let pin = pin.into_analog(); - let adc_config = AdcConfig::default() - .scan(Scan::Enabled) - .continuous(Continuous::Single) - .clock(Clock::Pclk2_div_2); - let mut adc = Adc::adc1(adc, true, adc_config); - - adc.configure_channel(&pin, Sequence::One, SampleTime::Cycles_480); - - AdcInput { _pin: pin, adc } - } - - /// Enable the ADC, - /// run a conversion - /// disable the ADC - pub fn read(&mut self) -> u16 { - let adc = &mut self.adc; - adc.enable(); - adc.clear_end_of_conversion_flag(); - adc.start_conversion(); - let sample = adc.current_sample(); - let result = adc.sample_to_millivolts(sample); - adc.wait_for_conversion_sequence(); - adc.disable(); - result - } -} diff --git a/src/led.rs b/src/led.rs deleted file mode 100644 index 7524c95..0000000 --- a/src/led.rs +++ /dev/null @@ -1,46 +0,0 @@ -use embedded_hal::digital::OutputPin; -use stm32f4xx_hal::gpio::{ - Output, PushPull, - gpiob::{PB0, PB7, PB14}, -}; - -type GreenPin = PB0>; -type BluePin = PB7>; -type RedPin = PB14>; - -pub struct Led { - pin: PIN, -} - -impl Led { - fn new(pin: PIN) -> Self { - Led { pin } - } - - pub fn on(&mut self) { - self.pin.set_high(); - } - - pub fn off(&mut self) { - self.pin.set_low(); - } -} - - -impl Led { - pub fn green(pin: GreenPin) -> Self { - Self::new(pin) - } -} - -impl Led { - pub fn blue(pin: BluePin) -> Self { - Self::new(pin) - } -} - -impl Led { - pub fn red(pin: RedPin) -> Self { - Self::new(pin) - } -} diff --git a/src/main.rs b/src/main.rs index 5d6c39c..34dbe24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use core::fmt::Write; use cortex_m::asm::wfi; use cortex_m_rt::entry; use embedded_hal::watchdog::{WatchdogEnable, Watchdog}; +use embedded_hal::digital::OutputPin; use stm32f4xx_hal::{ rcc::RccExt, gpio::GpioExt, @@ -25,20 +26,11 @@ use smoltcp::{ wire::EthernetAddress, }; -mod adc_input; -use adc_input::AdcInput; mod net; mod server; use server::Server; mod timer; -mod led; -use led::Led; -/// Interval at which to sample the ADC input and broadcast to all -/// clients. -/// -/// This should be a multiple of the `TIMER_RATE`. -const OUTPUT_INTERVAL: u32 = 1000; #[cfg(not(feature = "generate-hwaddr"))] const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; @@ -67,7 +59,7 @@ fn init_log() { #[entry] fn main() -> ! { init_log(); - info!("adc2tcp"); + info!("tnetplug"); let mut cp = CorePeripherals::take().unwrap(); cp.SCB.enable_icache(); @@ -90,14 +82,11 @@ fn main() -> ! { let gpioa = dp.GPIOA.split(); let gpiob = dp.GPIOB.split(); let gpioc = dp.GPIOC.split(); + let gpioe = dp.GPIOE.split(); let gpiog = dp.GPIOG.split(); - let mut led_green = Led::green(gpiob.pb0.into_push_pull_output()); - let mut led_blue = Led::blue(gpiob.pb7.into_push_pull_output()); - let mut led_red = Led::red(gpiob.pb14.into_push_pull_output()); - - info!("ADC init"); - let mut adc_input = AdcInput::new(dp.ADC1, gpioa.pa3); + let mut relay1 = gpioe.pe11.into_push_pull_output(); + let mut relay2 = gpioe.pe9.into_push_pull_output(); info!("Eth setup"); stm32_eth::setup_pins( @@ -120,25 +109,22 @@ fn main() -> ! { info!("Net startup"); net::run(&mut cp.NVIC, dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| { Server::run(iface, |server| { - let mut last_output = 0_u32; loop { let now = timer::now().0; let instant = Instant::from_millis(i64::from(now)); - led_blue.on(); cortex_m::interrupt::free(net::clear_pending); - server.poll(instant) + let cmd = server.poll(instant) .unwrap_or_else(|e| { warn!("poll: {:?}", e); + None }); - led_blue.off(); - let now = timer::now().0; - if now - last_output >= OUTPUT_INTERVAL { - led_red.on(); - let adc_value = adc_input.read(); - writeln!(server, "t={},pa3={}\r", now, adc_value).unwrap(); - last_output = now; - led_red.off(); + match cmd { + Some(b'a') => { relay1.set_low(); writeln!(server, "A=OFF").unwrap(); }, + Some(b'A') => { relay1.set_high(); writeln!(server, "A=ON").unwrap(); }, + Some(b'b') => { relay2.set_low(); writeln!(server, "B=OFF").unwrap(); }, + Some(b'B') => { relay2.set_high(); writeln!(server, "B=ON").unwrap(); }, + _ => (), } // Update watchdog @@ -146,10 +132,8 @@ fn main() -> ! { cortex_m::interrupt::free(|cs| { if !net::is_pending(cs) { - led_green.on(); // Wait for interrupts wfi(); - led_green.off(); } }); } diff --git a/src/net.rs b/src/net.rs index be51996..602e68b 100644 --- a/src/net.rs +++ b/src/net.rs @@ -43,7 +43,7 @@ pub fn run( eth_dev.enable_interrupt(nvic); // IP stack - let local_addr = IpAddress::v4(192, 168, 69, 3); + let local_addr = IpAddress::v4(192, 168, 1, 31); let mut ip_addrs = [IpCidr::new(local_addr, 24)]; let mut neighbor_storage = [None; 16]; let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]); diff --git a/src/server.rs b/src/server.rs index b1080f8..10a713c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use smoltcp::{ }; -const TCP_PORT: u16 = 23; +const TCP_PORT: u16 = 3131; /// Number of server sockets and therefore concurrent client /// sessions. Many data structures in `Server::run()` correspond to /// this const. @@ -32,8 +32,6 @@ macro_rules! create_socket { ) } -/// Contains a number of server sockets that get all sent the same -/// data (through `fmt::Write`). pub struct Server<'a, 'b> { net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>, sockets: SocketSet<'b, 'b, 'static>, @@ -79,7 +77,7 @@ impl<'a, 'b> Server<'a, 'b> { } /// Poll the interface and the sockets - pub fn poll(&mut self, now: Instant) -> Result<(), smoltcp::Error> { + pub fn poll(&mut self, now: Instant) -> Result, smoltcp::Error> { // Poll smoltcp EthernetInterface let mut poll_error = None; let activity = self.net.poll(&mut self.sockets, now) @@ -88,23 +86,33 @@ impl<'a, 'b> Server<'a, 'b> { true }); + // Pass some smoltcp errors to the caller + match poll_error { + None => (), + Some(smoltcp::Error::Malformed) => (), + Some(smoltcp::Error::Unrecognized) => (), + Some(e) => return Err(e), + } + + let mut ret = None; if activity { // Listen on all sockets for handle in &self.handles { let mut socket = self.sockets.get::(*handle); - if ! socket.is_open() { - let _ = socket.listen(TCP_PORT); + if !socket.is_open() { + socket.listen(TCP_PORT).unwrap(); + } + + if socket.may_recv() { + if ret.is_none() { + ret = socket.recv(|data| { if data.len() > 0 { (1, Some(data[0])) } else { (0, None) } }).unwrap(); + } + } else if socket.may_send() { + socket.close(); } } } - - // Pass some smoltcp errors to the caller - match poll_error { - None => Ok(()), - Some(smoltcp::Error::Malformed) => Ok(()), - Some(smoltcp::Error::Unrecognized) => Ok(()), - Some(e) => Err(e), - } + Ok(ret) } }