#![no_std]
use embedded_hal as hal;
use nb::block;
use core::cmp::min;
use core::fmt::{self};
use arrayvec::{CapacityError, ArrayString};
use itoa;
mod serial;
mod timing;
pub use timing::{LongTimer, Second, Millisecond};
const AT_RESPONSE_BUFFER_SIZE: usize = 13;
#[derive(Debug, PartialEq)]
pub enum ATResponse {
Ok,
Error,
Busy,
WiFiGotIp,
}
#[derive(Debug)]
pub enum Error<R, T, P> {
TxError(T),
RxError(R),
PinError(P),
UnexpectedResponse(ATResponse),
Fmt(fmt::Error),
Capacity(CapacityError)
}
impl<R,T, P> From<fmt::Error> for Error<R,T, P> {
fn from(other: fmt::Error) -> Error<R,T, P> {
Error::Fmt(other)
}
}
impl<R,T,ErrType,P> From<CapacityError<ErrType>> for Error<R,T,P> {
fn from(other: CapacityError<ErrType>) -> Error<R,T,P> {
Error::Capacity(other.simplify())
}
}
#[derive(Debug)]
pub enum TransmissionStep {
Connect,
Send,
Close
}
#[derive(Debug)]
pub struct TransmissionError<R, T, P> {
step: TransmissionStep,
cause: Error<R, T, P>
}
impl<R, T, P> TransmissionError<R, T, P> {
pub fn try_step<RetType>(step: TransmissionStep, cause: Result<RetType, Error<R, T, P>>)
-> Result<RetType, Self>
{
cause.map_err(|e| {
Self {
step,
cause: e
}
})
}
}
pub enum ConnectionType {
Tcp,
Udp
}
impl ConnectionType {
pub fn as_str(&self) -> &str {
match *self {
ConnectionType::Tcp => "TCP",
ConnectionType::Udp => "UDP"
}
}
}
macro_rules! return_type {
($ok:ty) => {
Result<$ok, Error<serial::Error<Rx::Error>, Tx::Error, Rst::Error>>
}
}
macro_rules! transmission_return_type {
($ok:ty) => {
Result<$ok, TransmissionError<serial::Error<Rx::Error>, Tx::Error, Rst::Error>>
}
}
const STARTUP_TIMEOUT: Second = Second(10);
const DEFAULT_TIMEOUT: Second = Second(5);
pub struct Esp8266<Tx, Rx, Timer, Rst>
where Tx: hal::serial::Write<u8>,
Rx: hal::serial::Read<u8>,
Timer: LongTimer,
Rst: hal::digital::v2::OutputPin
{
tx: Tx,
rx: Rx,
timer: Timer,
chip_enable_pin: Rst
}
impl<Tx, Rx, Timer, Rst> Esp8266<Tx, Rx, Timer, Rst>
where Tx: hal::serial::Write<u8>,
Rx: hal::serial::Read<u8>,
Timer: LongTimer,
Rst: hal::digital::v2::OutputPin,
{
pub fn new(tx: Tx, rx: Rx, timer: Timer, chip_enable_pin: Rst)
-> return_type!(Self)
{
let mut result = Self {tx, rx, timer, chip_enable_pin};
result.reset()?;
Ok(result)
}
pub fn send_data(
&mut self,
connection_type: ConnectionType,
address: &str,
port: u16,
data: &str
) -> transmission_return_type!(())
{
let tcp_start_result = self.start_tcp_connection(connection_type, address, port);
TransmissionError::try_step(TransmissionStep::Connect, tcp_start_result)?;
TransmissionError::try_step(TransmissionStep::Send, self.transmit_data(data))?;
TransmissionError::try_step(TransmissionStep::Close, self.close_connection())
}
pub fn close_connection(&mut self) -> return_type!(()) {
self.send_at_command("+CIPCLOSE")?;
self.wait_for_ok(DEFAULT_TIMEOUT.into())
}
pub fn power_down(&mut self) -> return_type!(()) {
self.chip_enable_pin.set_low().map_err(Error::PinError)
}
pub fn reset(&mut self) -> return_type!(()) {
self.power_down()?;
self.timer.start(Millisecond(10));
block!(self.timer.wait()).unwrap();
self.power_up()
}
pub fn power_up(&mut self) -> return_type!(()) {
self.chip_enable_pin.set_high().map_err(Error::PinError)?;
let mut error_count = 0;
loop {
match self.wait_for_got_ip(STARTUP_TIMEOUT.into()) {
Ok(()) => break,
e @ Err(Error::RxError(serial::Error::TimedOut)) => return e,
e => {
if error_count < 255 {
error_count += 1;
continue
}
else {
return e
}
}
}
}
self.send_at_command("E0")?;
self.wait_for_ok(DEFAULT_TIMEOUT.into())?;
Ok(())
}
pub fn pull_some_current(&mut self) -> return_type!(()) {
self.chip_enable_pin.set_high().map_err(Error::PinError)?;
self.timer.start(Millisecond(500));
block!(self.timer.wait()).unwrap();
self.chip_enable_pin.set_low().map_err(Error::PinError)
}
fn transmit_data(&mut self, data: &str) -> return_type!(()) {
self.start_transmission(data.len())?;
self.wait_for_prompt(DEFAULT_TIMEOUT.into())?;
self.send_raw(data.as_bytes())?;
self.wait_for_ok(DEFAULT_TIMEOUT.into())
}
fn start_tcp_connection (
&mut self,
connection_type: ConnectionType,
address: &str,
port: u16
) -> return_type!(())
{
const PORT_STRING_LENGTH: usize = 5;
let mut port_str = ArrayString::<[_;PORT_STRING_LENGTH]>::new();
itoa::fmt(&mut port_str, port)?;
self.send_raw("AT+CIPSTART=\"".as_bytes())?;
self.send_raw(connection_type.as_str().as_bytes())?;
self.send_raw("\",\"".as_bytes())?;
self.send_raw(address.as_bytes())?;
self.send_raw("\",".as_bytes())?;
self.send_raw(port_str.as_bytes())?;
self.send_raw("\r\n".as_bytes())?;
self.wait_for_ok(DEFAULT_TIMEOUT.into())
}
fn start_transmission(&mut self, message_length: usize) -> return_type!(()) {
assert!(message_length < 2048);
let mut length_buffer = ArrayString::<[_; 4]>::new();
itoa::fmt(&mut length_buffer, message_length)?;
self.send_raw(b"AT+CIPSEND=")?;
self.send_raw(length_buffer.as_bytes())?;
self.send_raw(b"\r\n")?;
Ok(())
}
fn send_at_command(&mut self, command: &str) -> return_type!(()) {
self.send_raw(b"AT")?;
self.send_raw(command.as_bytes())?;
self.send_raw(b"\r\n")?;
Ok(())
}
fn wait_for_at_response(
&mut self,
expected_response: &ATResponse,
timeout: Millisecond
) -> return_type!(()) {
let mut buffer = [0; AT_RESPONSE_BUFFER_SIZE];
let response = serial::read_until_message(
&mut self.rx,
&mut self.timer,
timeout,
&mut buffer,
&parse_at_response
);
match response {
Ok(ref resp) if resp == expected_response => {
Ok(())
},
Ok(other) => {
Err(Error::UnexpectedResponse(other))
}
Err(e) => {
Err(Error::RxError(e))
}
}
}
fn wait_for_ok(&mut self, timeout: Millisecond) -> return_type!(()) {
self.wait_for_at_response(&ATResponse::Ok, timeout)
}
fn wait_for_got_ip(&mut self, timeout: Millisecond) -> return_type!(()) {
self.wait_for_at_response(&ATResponse::WiFiGotIp, timeout)
}
fn wait_for_prompt(&mut self, timeout: Millisecond) -> return_type!(()) {
let mut buffer = [0; 1];
let result = serial::read_until_message(
&mut self.rx,
&mut self.timer,
timeout,
&mut buffer,
&|buf, _ptr| {
if buf[0] == '>' as u8 {
Some(())
}
else {
None
}
}
);
match result {
Ok(_) => Ok(()),
Err(e) => Err(Error::RxError(e))
}
}
fn send_raw(&mut self, bytes: &[u8]) -> return_type!(()) {
match serial::write_all(&mut self.tx, bytes) {
Ok(_) => Ok(()),
Err(e) => Err(Error::TxError(e))
}
}
}
pub fn parse_at_response(buffer: &[u8], offset: usize) -> Option<ATResponse> {
if compare_circular_buffer(buffer, offset, "OK\r\n".as_bytes()) {
Some(ATResponse::Ok)
}
else if compare_circular_buffer(buffer, offset, "ERROR\r\n".as_bytes()) {
Some(ATResponse::Error)
}
else if compare_circular_buffer(buffer, offset, "busy p...\r\n".as_bytes()) {
Some(ATResponse::Busy)
}
else if compare_circular_buffer(buffer, offset, "WIFI GOT IP\r\n".as_bytes()) {
Some(ATResponse::WiFiGotIp)
}
else {
None
}
}
pub fn compare_circular_buffer(
circular_buffer: &[u8],
offset: usize,
comparison: &[u8]
) -> bool
{
let comparison_length = min(circular_buffer.len(), comparison.len());
for i in 0..comparison_length {
let circular_index = (circular_buffer.len() + offset - 1 - i) % circular_buffer.len();
let comparison_index = comparison.len() - 1 - i;
if circular_buffer[circular_index] != comparison[comparison_index] {
return false;
}
}
true
}