add support for ipv4 address reconfiguration

This commit is contained in:
Astro 2020-10-01 01:34:46 +02:00
parent 5acebbef9f
commit 59103cb2a1
6 changed files with 95 additions and 11 deletions

View File

@ -76,3 +76,4 @@ The scope of this setting is per TCP session.
| `load` | Restore configuration from EEPROM | | `load` | Restore configuration from EEPROM |
| `save` | Save configuration to EEPROM | | `save` | Save configuration to EEPROM |
| `reset` | Reset the device | | `reset` | Reset the device |
| `ipv4 <X.X.X.X>` | Configure IPv4 address |

View File

@ -135,6 +135,7 @@ pub enum Command {
Load, Load,
Save, Save,
Reset, Reset,
Ipv4([u8; 4]),
Show(ShowCommand), Show(ShowCommand),
Reporting(bool), Reporting(bool),
/// PWM parameter setting /// PWM parameter setting
@ -181,6 +182,19 @@ fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
fold_many1(char(' '), (), |(), _| ())(input) fold_many1(char(' '), (), |(), _| ())(input)
} }
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
take_while1(is_digit)(input)
.map(|(input, digits)| {
let result =
from_utf8(digits)
.map_err(|e| e.into())
.and_then(|digits| u32::from_str_radix(digits, 10)
.map_err(|e| e.into())
);
(input, result)
})
}
fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> { fn float(input: &[u8]) -> IResult<&[u8], Result<f64, Error>> {
let (input, sign) = opt(is_a("-"))(input)?; let (input, sign) = opt(is_a("-"))(input)?;
let negative = sign.is_some(); let negative = sign.is_some();
@ -415,11 +429,30 @@ fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
))(input) ))(input)
} }
fn ipv4(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("ipv4")(input)?;
let (input, _) = whitespace(input)?;
let (input, a) = unsigned(input)?;
let (input, _) = tag(".")(input)?;
let (input, b) = unsigned(input)?;
let (input, _) = tag(".")(input)?;
let (input, c) = unsigned(input)?;
let (input, _) = tag(".")(input)?;
let (input, d) = unsigned(input)?;
end(input)?;
let result = a.and_then(|a| b.and_then(|b| c.and_then(|c| d.map(|d|
Command::Ipv4([a as u8, b as u8, c as u8, d as u8])
))));
Ok((input, result))
}
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
alt((value(Ok(Command::Quit), tag("quit")), alt((value(Ok(Command::Quit), tag("quit")),
value(Ok(Command::Load), tag("load")), value(Ok(Command::Load), tag("load")),
value(Ok(Command::Save), tag("save")), value(Ok(Command::Save), tag("save")),
value(Ok(Command::Reset), tag("reset")), value(Ok(Command::Reset), tag("reset")),
ipv4,
map(report, Ok), map(report, Ok),
pwm, pwm,
center_point, center_point,
@ -464,6 +497,12 @@ mod test {
assert_eq!(command, Ok(Command::Save)); assert_eq!(command, Ok(Command::Save));
} }
#[test]
fn parse_ipv4() {
let command = Command::parse(b"ipv4 192.168.1.26");
assert_eq!(command, Ok(Command::Ipv4([192, 168, 1, 26])));
}
#[test] #[test]
fn parse_report() { fn parse_report() {
let command = Command::parse(b"report"); let command = Command::parse(b"report");

View File

@ -1,5 +1,6 @@
use postcard::{from_bytes, to_slice}; use postcard::{from_bytes, to_slice};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use smoltcp::wire::Ipv4Address;
use stm32f4xx_hal::i2c; use stm32f4xx_hal::i2c;
use uom::si::{ use uom::si::{
electric_potential::volt, electric_potential::volt,
@ -40,15 +41,17 @@ impl From<postcard::Error> for Error {
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Config { pub struct Config {
channels: [ChannelConfig; CHANNELS], channels: [ChannelConfig; CHANNELS],
pub ipv4_address: [u8; 4],
} }
impl Config { impl Config {
pub fn new(channels: &mut Channels) -> Self { pub fn new(channels: &mut Channels, ipv4_address: Ipv4Address) -> Self {
Config { Config {
channels: [ channels: [
ChannelConfig::new(channels, 0), ChannelConfig::new(channels, 0),
ChannelConfig::new(channels, 1), ChannelConfig::new(channels, 1),
], ],
ipv4_address: ipv4_address.0,
} }
} }
@ -191,6 +194,7 @@ impl PwmLimits {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::DEFAULT_IPV4_ADDRESS;
#[test] #[test]
fn test_fit_eeprom() { fn test_fit_eeprom() {
@ -211,6 +215,7 @@ mod test {
channel_config.clone(), channel_config.clone(),
channel_config.clone(), channel_config.clone(),
], ],
ipv4_address: DEFAULT_IPV4_ADDRESS.0,
}; };
let mut buffer = [0; EEPROM_SIZE]; let mut buffer = [0; EEPROM_SIZE];
@ -237,6 +242,7 @@ mod test {
channel_config.clone(), channel_config.clone(),
channel_config.clone(), channel_config.clone(),
], ],
ipv4_address: DEFAULT_IPV4_ADDRESS.0,
}; };
let mut buffer = [0; EEPROM_SIZE]; let mut buffer = [0; EEPROM_SIZE];

View File

@ -24,7 +24,7 @@ use stm32f4xx_hal::{
use smoltcp::{ use smoltcp::{
time::Instant, time::Instant,
socket::TcpSocket, socket::TcpSocket,
wire::EthernetAddress, wire::{EthernetAddress, Ipv4Address},
}; };
use uom::{ use uom::{
si::{ si::{
@ -76,6 +76,7 @@ const WATCHDOG_INTERVAL: u32 = 30_000;
pub const EEPROM_PAGE_SIZE: usize = 8; pub const EEPROM_PAGE_SIZE: usize = 8;
pub const EEPROM_SIZE: usize = 128; pub const EEPROM_SIZE: usize = 128;
pub const DEFAULT_IPV4_ADDRESS: Ipv4Address = Ipv4Address([192, 168, 1, 26]);
const TCP_PORT: u16 = 23; const TCP_PORT: u16 = 23;
@ -159,10 +160,15 @@ fn main() -> ! {
usb::State::setup(usb); usb::State::setup(usb);
let mut ipv4_address = DEFAULT_IPV4_ADDRESS;
let mut channels = Channels::new(pins); let mut channels = Channels::new(pins);
let _ = Config::load(&mut eeprom) let _ = Config::load(&mut eeprom)
.map(|config| config.apply(&mut channels)) .map(|config| {
config.apply(&mut channels);
ipv4_address = Ipv4Address::from_bytes(&config.ipv4_address);
})
.map_err(|e| warn!("error loading config: {:?}", e)); .map_err(|e| warn!("error loading config: {:?}", e));
info!("IPv4 address: {}", ipv4_address);
// EEPROM ships with a read-only EUI-48 identifier // EEPROM ships with a read-only EUI-48 identifier
let mut eui48 = [0; 6]; let mut eui48 = [0; 6];
@ -170,7 +176,8 @@ fn main() -> ! {
let hwaddr = EthernetAddress(eui48); let hwaddr = EthernetAddress(eui48);
info!("EEPROM MAC address: {}", hwaddr); info!("EEPROM MAC address: {}", hwaddr);
net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, |iface| { net::run(clocks, dp.ETHERNET_MAC, dp.ETHERNET_DMA, eth_pins, hwaddr, ipv4_address, |iface| {
let mut new_ipv4_address = None;
Server::<Session>::run(iface, |server| { Server::<Session>::run(iface, |server| {
leds.r1.off(); leds.r1.off();
@ -339,20 +346,25 @@ fn main() -> ! {
} }
Command::Load => { Command::Load => {
match Config::load(&mut eeprom) { match Config::load(&mut eeprom) {
Ok(config) => Ok(config) => {
config.apply(&mut channels), config.apply(&mut channels);
new_ipv4_address = Some(Ipv4Address::from_bytes(&config.ipv4_address));
}
Err(e) => Err(e) =>
error!("unable to load eeprom config: {:?}", e), error!("unable to load eeprom config: {:?}", e),
} }
} }
Command::Save => { Command::Save => {
let config = Config::new(&mut channels); let config = Config::new(&mut channels, ipv4_address);
match config.save(&mut eeprom) { match config.save(&mut eeprom) {
Ok(()) => {}, Ok(()) => {},
Err(e) => Err(e) =>
error!("unable to save eeprom config: {:?}", e), error!("unable to save eeprom config: {:?}", e),
} }
} }
Command::Ipv4(address) => {
new_ipv4_address = Some(Ipv4Address::from_bytes(&address));
}
Command::Reset => { Command::Reset => {
for i in 0..CHANNELS { for i in 0..CHANNELS {
channels.power_down(i); channels.power_down(i);
@ -375,6 +387,11 @@ fn main() -> ! {
} }
}); });
// Apply new IPv4 address
new_ipv4_address.map(|ipv4_address|
server.set_ipv4_address(ipv4_address)
);
// Update watchdog // Update watchdog
wd.feed(); wd.feed();

View File

@ -8,7 +8,7 @@ use stm32f4xx_hal::{
rcc::Clocks, rcc::Clocks,
stm32::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA}, stm32::{interrupt, Peripherals, ETHERNET_MAC, ETHERNET_DMA},
}; };
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
use stm32_eth::{Eth, RingEntry, PhyAddress, RxDescriptor, TxDescriptor}; use stm32_eth::{Eth, RingEntry, PhyAddress, RxDescriptor, TxDescriptor};
use crate::pins::EthernetPins; use crate::pins::EthernetPins;
@ -29,7 +29,9 @@ pub fn run<F>(
clocks: Clocks, clocks: Clocks,
ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA, ethernet_mac: ETHERNET_MAC, ethernet_dma: ETHERNET_DMA,
eth_pins: EthernetPins, eth_pins: EthernetPins,
ethernet_addr: EthernetAddress, f: F ethernet_addr: EthernetAddress,
local_addr: Ipv4Address,
f: F
) where ) where
F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>), F: FnOnce(EthernetInterface<&mut stm32_eth::Eth<'static, 'static>>),
{ {
@ -50,8 +52,9 @@ pub fn run<F>(
eth_dev.enable_interrupt(); eth_dev.enable_interrupt();
// IP stack // IP stack
let local_addr = IpAddress::v4(192, 168, 1, 26); // Netmask 0 means we expect any IP address on the local segment.
let mut ip_addrs = [IpCidr::new(local_addr, 24)]; // No routing.
let mut ip_addrs = [IpCidr::new(local_addr.into(), 0)];
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[..]);
let iface = EthernetInterfaceBuilder::new(&mut eth_dev) let iface = EthernetInterfaceBuilder::new(&mut eth_dev)

View File

@ -3,6 +3,7 @@ use smoltcp::{
iface::EthernetInterface, iface::EthernetInterface,
socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef}, socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
time::Instant, time::Instant,
wire::{IpCidr, Ipv4Address, Ipv4Cidr},
}; };
@ -83,4 +84,21 @@ impl<'a, 'b, S: Default> Server<'a, 'b, S> {
callback(socket, &mut state.state); callback(socket, &mut state.state);
} }
} }
pub fn set_ipv4_address(&mut self, ipv4_address: Ipv4Address) {
self.net.update_ip_addrs(|addrs| {
for addr in addrs.iter_mut() {
match addr {
IpCidr::Ipv4(_) => {
*addr = IpCidr::Ipv4(Ipv4Cidr::new(ipv4_address, 0));
// done
break
}
_ => {
// skip
}
}
}
});
}
} }