stm32f1xx-hal 0.10.0

HAL for the STM32F1xx family of microcontrollers
Documentation
//! ENC28J60 demo: a RESTful LED using CoAP
//!
//! The server will expose the LED as a resource under the `/led` path. You can use the CoAP client
//! in the [`jnet`] crate to interact with the server.
//!
//! - `coap GET coap://192.168.1.33/led` will return the state of the LED: either "on" or "off".
//! - `coap PUT coap://192.168.1.33/led on` will change the state of the LED; the payload must be
//!   either "on" or "off".
//!
//! [`jnet`]: https://github.com/japaric/jnet

#![deny(unsafe_code)]
#![deny(warnings)]
#![feature(nll)]
#![feature(try_from)]
#![no_main]
#![no_std]

#[macro_use]
extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate enc28j60;
extern crate heapless;
extern crate jnet;
extern crate panic_itm;
#[macro_use]
extern crate serde_derive;
extern crate serde_json_core as json;
extern crate stm32f1xx_hal as hal;

use core::convert::TryInto;

use enc28j60::Enc28j60;
use hal::delay::Delay;
use hal::prelude::*;
use hal::spi::Spi;
use hal::stm32f103xx;
use heapless::consts::*;
use heapless::FnvIndexMap;
use jnet::{arp, coap, ether, icmp, ipv4, mac, udp, Buffer};
use rt::{entry, exception, ExceptionFrame};

/* Constants */
const KB: u16 = 1024;

/* Network configuration */
const MAC: mac::Addr = mac::Addr([0x20, 0x18, 0x03, 0x01, 0x00, 0x00]);
const IP: ipv4::Addr = ipv4::Addr([192, 168, 1, 33]);

// disable tracing
// macro_rules! iprintln {
//     ($($tt: tt)*) => {};
// }

// LED resource
#[derive(Deserialize, Serialize)]
struct Led {
    led: bool,
}

#[entry]
fn main() -> ! {
    let mut cp = cortex_m::Peripherals::take().unwrap();
    let dp = stm32f103xx::Peripherals::take().unwrap();

    let mut rcc = dp.RCC.constrain();
    let mut afio = dp.AFIO.constrain();
    let mut flash = dp.FLASH.constrain();
    let mut gpioa = dp.GPIOA.split();
    let _stim = &mut cp.ITM.stim[0];

    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    // LED
    let mut gpioc = dp.GPIOC.split();
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    // turn the LED off during initialization
    led.set_high();

    // SPI
    let mut rst = gpioa.pa3.into_push_pull_output(&mut gpioa.crl);
    rst.set_high();
    let mut ncs = gpioa.pa4.into_push_pull_output(&mut gpioa.crl);
    ncs.set_high();
    let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl);
    let miso = gpioa.pa6;
    let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl);
    let spi = Spi::spi1(
        dp.SPI1,
        (sck, miso, mosi),
        &mut afio.mapr,
        enc28j60::MODE,
        1.MHz(),
        clocks,
    );

    // ENC28J60
    let mut delay = Delay::new(cp.SYST, &clocks);
    let mut enc28j60 = Enc28j60::new(
        spi,
        ncs,
        enc28j60::Unconnected,
        rst,
        &mut delay,
        7 * KB,
        MAC.0,
    )
    .ok()
    .unwrap();

    // LED on after initialization
    led.set_low();

    // FIXME some frames are lost when sending right after initialization
    delay.delay_ms(100_u8);

    let mut cache = FnvIndexMap::<_, _, U8>::new();

    let mut buf = [0; 128];
    loop {
        let mut buf = Buffer::new(&mut buf);
        let len = enc28j60.receive(buf.as_mut()).ok().unwrap();
        buf.truncate(len);

        if let Ok(mut eth) = ether::Frame::parse(buf) {
            iprintln!(_stim, "\nRx({})", eth.as_bytes().len());
            iprintln!(_stim, "* {:?}", eth);

            let mac_src = eth.get_source();

            match eth.get_type() {
                ether::Type::Arp => {
                    if let Ok(arp) = arp::Packet::parse(eth.payload_mut()) {
                        match arp.downcast() {
                            Ok(mut arp) => {
                                iprintln!(_stim, "** {:?}", arp);

                                if !arp.is_a_probe() {
                                    cache.insert(arp.get_spa(), arp.get_sha()).ok();
                                }

                                // are they asking for us?
                                if arp.get_oper() == arp::Operation::Request && arp.get_tpa() == IP
                                {
                                    // reply the ARP request
                                    let tha = arp.get_sha();
                                    let tpa = arp.get_spa();

                                    arp.set_oper(arp::Operation::Reply);
                                    arp.set_sha(MAC);
                                    arp.set_spa(IP);
                                    arp.set_tha(tha);
                                    arp.set_tpa(tpa);
                                    iprintln!(_stim, "\n** {:?}", arp);

                                    // update the Ethernet header
                                    eth.set_destination(tha);
                                    eth.set_source(MAC);
                                    iprintln!(_stim, "* {:?}", eth);

                                    iprintln!(_stim, "Tx({})", eth.as_bytes().len());
                                    enc28j60.transmit(eth.as_bytes()).ok().unwrap();
                                }
                            }
                            Err(_arp) => {
                                iprintln!(_stim, "** {:?}", _arp);
                            }
                        }
                    } else {
                        iprintln!(_stim, "Err(B)");
                    }
                }
                ether::Type::Ipv4 => {
                    if let Ok(mut ip) = ipv4::Packet::parse(eth.payload_mut()) {
                        iprintln!(_stim, "** {:?}", ip);

                        let ip_src = ip.get_source();

                        if !mac_src.is_broadcast() {
                            cache.insert(ip_src, mac_src).ok();
                        }

                        match ip.get_protocol() {
                            ipv4::Protocol::Icmp => {
                                if let Ok(icmp) = icmp::Packet::parse(ip.payload_mut()) {
                                    iprintln!(_stim, "*** {:?}", icmp);

                                    if icmp.get_type() == icmp::Type::EchoRequest
                                        && icmp.get_code() == 0
                                    {
                                        let _icmp =
                                            icmp.set_type(icmp::Type::EchoReply).update_checksum();
                                        iprintln!(_stim, "\n*** {:?}", _icmp);

                                        // update the IP header
                                        let mut ip = ip.set_source(IP);
                                        ip.set_destination(ip_src);
                                        let _ip = ip.update_checksum();
                                        iprintln!(_stim, "** {:?}", _ip);

                                        // update the Ethernet header
                                        eth.set_destination(*cache.get(&ip_src).unwrap());
                                        eth.set_source(MAC);
                                        iprintln!(_stim, "* {:?}", eth);

                                        iprintln!(_stim, "Tx({})", eth.as_bytes().len());
                                        enc28j60.transmit(eth.as_bytes()).ok().unwrap();
                                    }
                                } else {
                                    iprintln!(_stim, "Err(C)");
                                }
                            }
                            ipv4::Protocol::Udp => {
                                if let Ok(udp) = udp::Packet::parse(ip.payload()) {
                                    iprintln!(_stim, "*** {:?}", udp);

                                    let udp_src = udp.get_source();

                                    if udp.get_destination() == coap::PORT {
                                        if let Ok(coap) = coap::Message::parse(udp.payload()) {
                                            iprintln!(_stim, "**** {:?}", coap);

                                            let path_is_led = coap
                                                .options()
                                                .filter_map(|opt| {
                                                    if opt.number() == coap::OptionNumber::UriPath {
                                                        Some(opt.value())
                                                    } else {
                                                        None
                                                    }
                                                })
                                                .eq([b"led"].iter().cloned());

                                            let mut resp = coap::Response::BadRequest;

                                            match coap.get_code().try_into() {
                                                Ok(coap::Method::Get) => {
                                                    if path_is_led {
                                                        resp = coap::Response::Content;
                                                    }
                                                }
                                                Ok(coap::Method::Put) => {
                                                    if path_is_led {
                                                        if let Ok(json) = json::de::from_slice::<Led>(
                                                            coap.payload(),
                                                        ) {
                                                            if json.led {
                                                                led.set_low();
                                                            } else {
                                                                led.set_high();
                                                            }
                                                            resp = coap::Response::Changed;
                                                        }
                                                    }
                                                }
                                                _ => {}
                                            }

                                            let mut buf = eth.free();
                                            buf.reset();
                                            let mut eth = ether::Frame::new(buf);
                                            eth.set_destination(*cache.get(&ip_src).unwrap());
                                            eth.set_source(MAC);

                                            eth.ipv4(|ip| {
                                                ip.set_source(IP);
                                                ip.set_destination(ip_src);

                                                ip.udp(|udp| {
                                                    udp.set_destination(udp_src);
                                                    udp.set_source(coap::PORT);
                                                    udp.coap(0, |coap| {
                                                        coap.set_type(coap::Type::Acknowledgement);
                                                        coap.set_code(resp);

                                                        if resp == coap::Response::Content {
                                                            coap.set_payload(
                                                                &json::ser::to_vec::<[u8; 16], _>(
                                                                    &Led {
                                                                        led: led.is_set_low(),
                                                                    },
                                                                )
                                                                .unwrap(),
                                                            );
                                                        } else {
                                                            coap.set_payload(&[]);
                                                        }

                                                        iprintln!(_stim, "\n**** {:?}", coap);
                                                    });

                                                    iprintln!(_stim, "*** {:?}", udp);
                                                });

                                                iprintln!(_stim, "** {:?}", ip);
                                            });

                                            iprintln!(_stim, "* {:?}", eth);

                                            let bytes = eth.as_bytes();
                                            iprintln!(_stim, "Tx({})", bytes.len());
                                            enc28j60.transmit(bytes).ok().unwrap();
                                        }
                                    }
                                }
                            }
                            _ => {}
                        }
                    } else {
                        iprintln!(_stim, "Err(D)");
                    }
                }
                _ => {}
            }
        } else {
            iprintln!(_stim, "Err(E)");
        }
    }
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

#[exception]
fn DefaultHandler(irqn: i16) {
    panic!("Unhandled exception (IRQn = {})", irqn);
}