first tnetplug version
parent
91c27d2499
commit
8b35c4260a
|
@ -1,23 +1,5 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# 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]]
|
[[package]]
|
||||||
name = "aligned"
|
name = "aligned"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -300,6 +282,24 @@ dependencies = [
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,18 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
categories = ["embedded", "no-std"]
|
categories = ["embedded", "no-std"]
|
||||||
name = "adc2tcp"
|
name = "tnetplug"
|
||||||
description = "Poll ADC pin, report over TCP"
|
description = "Trivial network-controlled plugs"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
authors = ["Astro <astro@spaceboyz.net>"]
|
authors = ["Sebastien Bourdeauducq <sb@m-labs.hk>"]
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
keywords = ["ethernet", "eth", "stm32", "adc", "tcp"]
|
keywords = ["ethernet", "eth", "stm32", "smartplug", "internetofshit"]
|
||||||
repository = "https://github.com/m-labs/adc2tcp"
|
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[badges]
|
|
||||||
travis-ci = { repository = "astro/adc2tcp", branch = "master" }
|
|
||||||
maintenance = { status = "experimental" }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = []
|
features = []
|
||||||
default-target = "thumbv7em-none-eabihf"
|
default-target = "thumbv7em-none-eabihf"
|
||||||
|
|
63
README.md
63
README.md
|
@ -1,66 +1,21 @@
|
||||||
# Synopsis
|
Trivial network-controlled plugs
|
||||||
|
================================
|
||||||
Exposes readings from an ADC pin (currently: *PA3*) of the board via a
|
|
||||||
TCP service on the Ethernet port.
|
|
||||||
|
|
||||||
|
|
||||||
# Network Protocol
|
Instructions
|
||||||
|
------------
|
||||||
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
|
|
||||||
|
|
||||||
![Made for NixOS](https://nixos.org/logo/nixos-lores.png)
|
![Made for NixOS](https://nixos.org/logo/nixos-lores.png)
|
||||||
|
|
||||||
## Build the firmware with `default.nix`
|
Build the firmware with `default.nix`
|
||||||
|
+++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
* `nix-build`
|
* `nix-build`
|
||||||
* This uses **cargo-vendor** to bundle dependencies, so that unstable versions from git can be used.
|
* 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`
|
* `nix-shell`
|
||||||
* Spawning `openocd`, the devboard should be connected already.
|
* Spawning `openocd`, the devboard should be connected already.
|
||||||
|
|
12
default.nix
12
default.nix
|
@ -11,13 +11,13 @@ let
|
||||||
rustPlatform = recurseIntoAttrs (callPackage ./nix/rustPlatform.nix {
|
rustPlatform = recurseIntoAttrs (callPackage ./nix/rustPlatform.nix {
|
||||||
inherit rustManifest;
|
inherit rustManifest;
|
||||||
});
|
});
|
||||||
adc2tcp = callPackage ./nix/adc2tcp.nix { inherit rustPlatform; };
|
tnetplug = callPackage ./nix/tnetplug.nix { inherit rustPlatform; };
|
||||||
openocd = callPackage ./nix/openocd.nix {};
|
openocd = callPackage ./nix/openocd.nix {};
|
||||||
in
|
in
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "adc2tcp-dist";
|
name = "tnetplug-dist";
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
adc2tcp
|
tnetplug
|
||||||
openocd
|
openocd
|
||||||
makeWrapper
|
makeWrapper
|
||||||
];
|
];
|
||||||
|
@ -26,7 +26,7 @@ stdenv.mkDerivation {
|
||||||
|
|
||||||
installPhase =
|
installPhase =
|
||||||
let
|
let
|
||||||
firmwareBinary = "$out/lib/adc2tcp.elf";
|
firmwareBinary = "$out/lib/tnetplug.elf";
|
||||||
openOcdFlags = [
|
openOcdFlags = [
|
||||||
"-c" "reset halt"
|
"-c" "reset halt"
|
||||||
"-c" "flash write_image erase ${firmwareBinary}"
|
"-c" "flash write_image erase ${firmwareBinary}"
|
||||||
|
@ -37,9 +37,9 @@ stdenv.mkDerivation {
|
||||||
in ''
|
in ''
|
||||||
mkdir -p $out/bin $out/lib $out/nix-support
|
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}"
|
--add-flags "${lib.escapeShellArgs openOcdFlags}"
|
||||||
|
|
||||||
echo file binary-dist ${firmwareBinary} >> $out/nix-support/hydra-build-products
|
echo file binary-dist ${firmwareBinary} >> $out/nix-support/hydra-build-products
|
||||||
|
|
|
@ -2,33 +2,33 @@
|
||||||
|
|
||||||
with rustPlatform;
|
with rustPlatform;
|
||||||
let
|
let
|
||||||
sha256 = "1i9p5d5n01ajbp8lmavyway6vr1mmy107qnccff9glvr91rqx352";
|
sha256 = "19cdc0lkm9247n6kf23ki66gysz530j1x2lfnzq7n0cpcs53q3h3";
|
||||||
fetchcargo = import ./fetchcargo.nix {
|
fetchcargo = import ./fetchcargo.nix {
|
||||||
inherit stdenv cacert git cargo-vendor;
|
inherit stdenv cacert git cargo-vendor;
|
||||||
inherit (rust) cargo;
|
inherit (rust) cargo;
|
||||||
};
|
};
|
||||||
adc2tcpDeps = fetchcargo {
|
tnetplugDeps = fetchcargo {
|
||||||
name = "adc2tcp";
|
name = "tnetplug";
|
||||||
src = ../.;
|
src = ../.;
|
||||||
inherit sha256;
|
inherit sha256;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
buildRustPackage rec {
|
buildRustPackage rec {
|
||||||
name = "adc2tcp";
|
name = "tnetplug";
|
||||||
version = "0.0.0";
|
version = "0.0.0";
|
||||||
|
|
||||||
src = ../.;
|
src = ../.;
|
||||||
cargoSha256 = sha256;
|
cargoSha256 = sha256;
|
||||||
|
|
||||||
buildInputs = [ adc2tcpDeps ];
|
buildInputs = [ tnetplugDeps ];
|
||||||
patchPhase = ''
|
patchPhase = ''
|
||||||
cat >> .cargo/config <<EOF
|
cat >> .cargo/config <<EOF
|
||||||
[source.crates-io]
|
[source.crates-io]
|
||||||
replace-with = "vendored-sources"
|
replace-with = "vendored-sources"
|
||||||
|
|
||||||
[source.vendored-sources]
|
[source.vendored-sources]
|
||||||
directory = "${adc2tcpDeps}"
|
directory = "${tnetplugDeps}"
|
||||||
EOF
|
EOF
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -40,6 +40,6 @@ buildRustPackage rec {
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/lib
|
mkdir -p $out/lib
|
||||||
cp target/thumbv7em-none-eabihf/release/adc2tcp $out/lib/
|
cp target/thumbv7em-none-eabihf/release/tnetplug $out/lib/
|
||||||
'';
|
'';
|
||||||
}
|
}
|
|
@ -5,11 +5,11 @@
|
||||||
|
|
||||||
with pkgs;
|
with pkgs;
|
||||||
let
|
let
|
||||||
adc2tcp = callPackage ./default.nix {
|
tnetplug = callPackage ./default.nix {
|
||||||
inherit rustManifest;
|
inherit rustManifest;
|
||||||
mozillaOverlay = import <mozillaOverlay>;
|
mozillaOverlay = import <mozillaOverlay>;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
build = lib.hydraJob adc2tcp;
|
build = lib.hydraJob tnetplug;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ let
|
||||||
openocd = callPackage ./nix/openocd.nix {};
|
openocd = callPackage ./nix/openocd.nix {};
|
||||||
in
|
in
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "adc2tcp-env";
|
name = "tnetplug-env";
|
||||||
buildInputs = with rustPlatform.rust; [
|
buildInputs = with rustPlatform.rust; [
|
||||||
rustc cargo pkgs.gdb
|
rustc cargo pkgs.gdb
|
||||||
];
|
];
|
||||||
|
|
|
@ -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<Analog>,
|
|
||||||
adc: Adc<ADC>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AdcInput {
|
|
||||||
/// Configure pin into analog mode
|
|
||||||
pub fn new<MODE>(adc: ADC, pin: Pin<MODE>) -> 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
|
|
||||||
}
|
|
||||||
}
|
|
46
src/led.rs
46
src/led.rs
|
@ -1,46 +0,0 @@
|
||||||
use embedded_hal::digital::OutputPin;
|
|
||||||
use stm32f4xx_hal::gpio::{
|
|
||||||
Output, PushPull,
|
|
||||||
gpiob::{PB0, PB7, PB14},
|
|
||||||
};
|
|
||||||
|
|
||||||
type GreenPin = PB0<Output<PushPull>>;
|
|
||||||
type BluePin = PB7<Output<PushPull>>;
|
|
||||||
type RedPin = PB14<Output<PushPull>>;
|
|
||||||
|
|
||||||
pub struct Led<PIN> {
|
|
||||||
pin: PIN,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<PIN: OutputPin> Led<PIN> {
|
|
||||||
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<GreenPin> {
|
|
||||||
pub fn green(pin: GreenPin) -> Self {
|
|
||||||
Self::new(pin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Led<BluePin> {
|
|
||||||
pub fn blue(pin: BluePin) -> Self {
|
|
||||||
Self::new(pin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Led<RedPin> {
|
|
||||||
pub fn red(pin: RedPin) -> Self {
|
|
||||||
Self::new(pin)
|
|
||||||
}
|
|
||||||
}
|
|
42
src/main.rs
42
src/main.rs
|
@ -13,6 +13,7 @@ use core::fmt::Write;
|
||||||
use cortex_m::asm::wfi;
|
use cortex_m::asm::wfi;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::watchdog::{WatchdogEnable, Watchdog};
|
use embedded_hal::watchdog::{WatchdogEnable, Watchdog};
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
use stm32f4xx_hal::{
|
use stm32f4xx_hal::{
|
||||||
rcc::RccExt,
|
rcc::RccExt,
|
||||||
gpio::GpioExt,
|
gpio::GpioExt,
|
||||||
|
@ -25,20 +26,11 @@ use smoltcp::{
|
||||||
wire::EthernetAddress,
|
wire::EthernetAddress,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod adc_input;
|
|
||||||
use adc_input::AdcInput;
|
|
||||||
mod net;
|
mod net;
|
||||||
mod server;
|
mod server;
|
||||||
use server::Server;
|
use server::Server;
|
||||||
mod timer;
|
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"))]
|
#[cfg(not(feature = "generate-hwaddr"))]
|
||||||
const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
||||||
|
@ -67,7 +59,7 @@ fn init_log() {
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
init_log();
|
init_log();
|
||||||
info!("adc2tcp");
|
info!("tnetplug");
|
||||||
|
|
||||||
let mut cp = CorePeripherals::take().unwrap();
|
let mut cp = CorePeripherals::take().unwrap();
|
||||||
cp.SCB.enable_icache();
|
cp.SCB.enable_icache();
|
||||||
|
@ -90,14 +82,11 @@ fn main() -> ! {
|
||||||
let gpioa = dp.GPIOA.split();
|
let gpioa = dp.GPIOA.split();
|
||||||
let gpiob = dp.GPIOB.split();
|
let gpiob = dp.GPIOB.split();
|
||||||
let gpioc = dp.GPIOC.split();
|
let gpioc = dp.GPIOC.split();
|
||||||
|
let gpioe = dp.GPIOE.split();
|
||||||
let gpiog = dp.GPIOG.split();
|
let gpiog = dp.GPIOG.split();
|
||||||
|
|
||||||
let mut led_green = Led::green(gpiob.pb0.into_push_pull_output());
|
let mut relay1 = gpioe.pe11.into_push_pull_output();
|
||||||
let mut led_blue = Led::blue(gpiob.pb7.into_push_pull_output());
|
let mut relay2 = gpioe.pe9.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);
|
|
||||||
|
|
||||||
info!("Eth setup");
|
info!("Eth setup");
|
||||||
stm32_eth::setup_pins(
|
stm32_eth::setup_pins(
|
||||||
|
@ -120,25 +109,22 @@ fn main() -> ! {
|
||||||
info!("Net startup");
|
info!("Net startup");
|
||||||
net::run(&mut cp.NVIC, dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
|
net::run(&mut cp.NVIC, dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
|
||||||
Server::run(iface, |server| {
|
Server::run(iface, |server| {
|
||||||
let mut last_output = 0_u32;
|
|
||||||
loop {
|
loop {
|
||||||
let now = timer::now().0;
|
let now = timer::now().0;
|
||||||
let instant = Instant::from_millis(i64::from(now));
|
let instant = Instant::from_millis(i64::from(now));
|
||||||
led_blue.on();
|
|
||||||
cortex_m::interrupt::free(net::clear_pending);
|
cortex_m::interrupt::free(net::clear_pending);
|
||||||
server.poll(instant)
|
let cmd = server.poll(instant)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
warn!("poll: {:?}", e);
|
warn!("poll: {:?}", e);
|
||||||
|
None
|
||||||
});
|
});
|
||||||
led_blue.off();
|
|
||||||
|
|
||||||
let now = timer::now().0;
|
match cmd {
|
||||||
if now - last_output >= OUTPUT_INTERVAL {
|
Some(b'a') => { relay1.set_low(); writeln!(server, "A=OFF").unwrap(); },
|
||||||
led_red.on();
|
Some(b'A') => { relay1.set_high(); writeln!(server, "A=ON").unwrap(); },
|
||||||
let adc_value = adc_input.read();
|
Some(b'b') => { relay2.set_low(); writeln!(server, "B=OFF").unwrap(); },
|
||||||
writeln!(server, "t={},pa3={}\r", now, adc_value).unwrap();
|
Some(b'B') => { relay2.set_high(); writeln!(server, "B=ON").unwrap(); },
|
||||||
last_output = now;
|
_ => (),
|
||||||
led_red.off();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update watchdog
|
// Update watchdog
|
||||||
|
@ -146,10 +132,8 @@ fn main() -> ! {
|
||||||
|
|
||||||
cortex_m::interrupt::free(|cs| {
|
cortex_m::interrupt::free(|cs| {
|
||||||
if !net::is_pending(cs) {
|
if !net::is_pending(cs) {
|
||||||
led_green.on();
|
|
||||||
// Wait for interrupts
|
// Wait for interrupts
|
||||||
wfi();
|
wfi();
|
||||||
led_green.off();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub fn run<F>(
|
||||||
eth_dev.enable_interrupt(nvic);
|
eth_dev.enable_interrupt(nvic);
|
||||||
|
|
||||||
// IP stack
|
// 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 ip_addrs = [IpCidr::new(local_addr, 24)];
|
||||||
let mut neighbor_storage = [None; 16];
|
let mut neighbor_storage = [None; 16];
|
||||||
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);
|
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);
|
||||||
|
|
|
@ -7,7 +7,7 @@ use smoltcp::{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const TCP_PORT: u16 = 23;
|
const TCP_PORT: u16 = 3131;
|
||||||
/// Number of server sockets and therefore concurrent client
|
/// Number of server sockets and therefore concurrent client
|
||||||
/// sessions. Many data structures in `Server::run()` correspond to
|
/// sessions. Many data structures in `Server::run()` correspond to
|
||||||
/// this const.
|
/// 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> {
|
pub struct Server<'a, 'b> {
|
||||||
net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>,
|
net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>,
|
||||||
sockets: SocketSet<'b, 'b, 'static>,
|
sockets: SocketSet<'b, 'b, 'static>,
|
||||||
|
@ -79,7 +77,7 @@ impl<'a, 'b> Server<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll the interface and the sockets
|
/// Poll the interface and the sockets
|
||||||
pub fn poll(&mut self, now: Instant) -> Result<(), smoltcp::Error> {
|
pub fn poll(&mut self, now: Instant) -> Result<Option<u8>, smoltcp::Error> {
|
||||||
// Poll smoltcp EthernetInterface
|
// Poll smoltcp EthernetInterface
|
||||||
let mut poll_error = None;
|
let mut poll_error = None;
|
||||||
let activity = self.net.poll(&mut self.sockets, now)
|
let activity = self.net.poll(&mut self.sockets, now)
|
||||||
|
@ -88,23 +86,33 @@ impl<'a, 'b> Server<'a, 'b> {
|
||||||
true
|
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 {
|
if activity {
|
||||||
// Listen on all sockets
|
// Listen on all sockets
|
||||||
for handle in &self.handles {
|
for handle in &self.handles {
|
||||||
let mut socket = self.sockets.get::<TcpSocket>(*handle);
|
let mut socket = self.sockets.get::<TcpSocket>(*handle);
|
||||||
if ! socket.is_open() {
|
if !socket.is_open() {
|
||||||
let _ = socket.listen(TCP_PORT);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(ret)
|
||||||
// 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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue