#![no_std]
#![deny(missing_docs, unsafe_code, unstable_features, unused_import_braces, unused_qualifications, trivial_casts,
trivial_numeric_casts)]
use crate::cayenne_lpp_into_iterator::CayenneLPPIntoFailableIterator;
pub use crate::cayenne_lpp_scalar::{CayenneLPPScalar, CayenneLPPValue};
pub use crate::constants::*;
use crate::error::Error;
pub(crate) mod constants;
pub(crate) mod cayenne_lpp_scalar;
mod cayenne_lpp_into_iterator;
pub mod error;
#[cfg(test)]
mod tests;
pub struct CayenneLPP<'a> {
buffer: &'a mut [u8],
index: usize
}
impl<'a> CayenneLPP<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
CayenneLPP {
buffer,
index: 0
}
}
pub fn reset(&mut self) {
self.index = 0;
}
pub fn payload_slice(&self) -> &[u8] {
&self.buffer[0..self.index]
}
pub fn add_scalar(&mut self, scalar: &CayenneLPPScalar) -> Result<(), Error> {
let channel = scalar.channel;
match scalar.value {
CayenneLPPValue::DigitalInput(s) => self.add_digital_input(channel, s),
CayenneLPPValue::DigitalOutput(s) => self.add_digital_output(channel, s),
CayenneLPPValue::AnalogInput(s) => self.add_analog_input(channel, s),
CayenneLPPValue::AnalogOutput(s) => self.add_analog_output(channel, s),
CayenneLPPValue::GenericSensor(s) => self.add_generic_sensor(channel, s),
CayenneLPPValue::Luminosity(s) => self.add_luminosity(channel, s),
CayenneLPPValue::Presence(s) => self.add_presence(channel, s),
CayenneLPPValue::Temperature(s) => self.add_temperature(channel, s),
CayenneLPPValue::RelativeHumidity(s) => self.add_relative_humidity(channel, s),
CayenneLPPValue::Accelerometer(x, y, z) => self.add_accelerometer(channel, x, y, z),
CayenneLPPValue::BarometricPressure(s) => self.add_barometric_pressure(channel, s),
CayenneLPPValue::Voltage(s) => self.add_voltage(channel, s),
CayenneLPPValue::Current(s) => self.add_current(channel, s),
CayenneLPPValue::Frequency(s) => self.add_frequency(channel, s),
CayenneLPPValue::Percentage(s) => self.add_percentage(channel, s),
CayenneLPPValue::Altitude(s) => self.add_altitude(channel, s),
CayenneLPPValue::Concentration(s) => self.add_concentration(channel, s),
CayenneLPPValue::Power(s) => self.add_power(channel, s),
CayenneLPPValue::Distance(s) => self.add_distance(channel, s),
CayenneLPPValue::Energy(s) => self.add_energy(channel, s),
CayenneLPPValue::Direction(s) => self.add_direction(channel, s),
CayenneLPPValue::UnixTime(s) => self.add_unixtime(channel, s),
CayenneLPPValue::Gyrometer(x, y, z) => self.add_gyrometer(channel, x, y, z),
CayenneLPPValue::Color(r, g, b) => self.add_color(channel, r, g, b),
CayenneLPPValue::GPS(lat, lon, alt) => self.add_gps(channel, lat, lon, alt),
CayenneLPPValue::Switch(s) => self.add_switch(channel, s),
}
}
pub fn add_digital_input(&mut self, channel: u8, value: u8) -> Result<(), Error> {
if self.index + LPP_DIGITAL_INPUT_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_DIGITAL_INPUT;
self.buffer[{ self.index += 1; self.index }] = value;
self.index += 1;
Ok(())
}
pub fn add_digital_output(&mut self, channel: u8, value: u8) -> Result<(), Error> {
if self.index + LPP_DIGITAL_OUTPUT_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_DIGITAL_OUTPUT;
self.buffer[{ self.index += 1; self.index }] = value;
self.index += 1;
Ok(())
}
pub fn add_analog_input(&mut self, channel: u8, value: f32) -> Result<(), Error> {
if self.index + LPP_ANALOG_INPUT_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let analog_input: i16 = (value * 100.0) as i16;
let analog_input_bytes = analog_input.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_ANALOG_INPUT;
self.buffer[{ self.index += 1; self.index }] = analog_input_bytes[0];
self.buffer[{ self.index += 1; self.index }] = analog_input_bytes[1];
self.index += 1;
Ok(())
}
pub fn add_analog_output(&mut self, channel: u8, value: f32) -> Result<(), Error> {
if self.index + LPP_ANALOG_OUTPUT_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let analog_output: i16 = (value * 100.0) as i16;
let analog_output_bytes = analog_output.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_ANALOG_OUTPUT;
self.buffer[{ self.index += 1; self.index }] = analog_output_bytes[0];
self.buffer[{ self.index += 1; self.index }] = analog_output_bytes[1];
self.index += 1;
Ok(())
}
pub fn add_generic_sensor(&mut self, channel: u8, value: u32) -> Result<(), Error> {
if self.index + LPP_GENERIC_SENSOR_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let value_bytes = value.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_GENERIC_SENSOR;
self.buffer[{ self.index += 1; self.index }] = value_bytes[0];
self.buffer[{ self.index += 1; self.index }] = value_bytes[1];
self.buffer[{ self.index += 1; self.index }] = value_bytes[2];
self.buffer[{ self.index += 1; self.index }] = value_bytes[3];
self.index += 1;
Ok(())
}
pub fn add_luminosity(&mut self, channel: u8, lux: u16) -> Result<(), Error> {
if self.index + LPP_LUMINOSITY_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_LUMINOSITY;
self.buffer[{ self.index += 1; self.index }] = (lux >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = lux as u8;
self.index += 1;
Ok(())
}
pub fn add_presence(&mut self, channel: u8, value: u8) -> Result<(), Error> {
if self.index + LPP_PRESENCE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_PRESENCE;
self.buffer[{ self.index += 1; self.index }] = value;
self.index += 1;
Ok(())
}
pub fn add_temperature(&mut self, channel: u8, celsius: f32) -> Result<(), Error> {
if self.index + LPP_TEMPERATURE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let temperature: i16 = (celsius * 10.0) as i16;
let temperature_bytes = temperature.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_TEMPERATURE;
self.buffer[{ self.index += 1; self.index }] = temperature_bytes[0];
self.buffer[{ self.index += 1; self.index }] = temperature_bytes[1];
self.index += 1;
Ok(())
}
pub fn add_relative_humidity(&mut self, channel: u8, relative_humidity: f32) -> Result<(), Error> {
if self.index + LPP_RELATIVE_HUMIDITY_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let scaled_value = relative_humidity * 2.0;
let fraction = scaled_value % 1.0;
let whole_number = scaled_value - fraction;
let rounded_value = match (whole_number, fraction) {
(s, f) if s > 0.0 && f < 0.5 => {
s
},
(s, f) if s > 0.0 && f >= 0.5 => {
s + 1.0
}
(s, f) if s < 0.0 && f > -0.5 => {
s
},
(s, f) if s < 0.0 && f <= -0.5 => {
s - 1.0
},
(s, _) => {
s
}
};
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_RELATIVE_HUMIDITY;
self.buffer[{ self.index += 1; self.index }] = rounded_value as u8;
self.index += 1;
Ok(())
}
pub fn add_accelerometer(&mut self, channel: u8, x: f32, y: f32, z: f32) -> Result<(), Error> {
if self.index + LPP_ACCELEROMETER_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let vx: i16 = (x * 1000.0) as i16;
let vy: i16 = (y * 1000.0) as i16;
let vz: i16 = (z * 1000.0) as i16;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_ACCELEROMETER;
self.buffer[{ self.index += 1; self.index }] = (vx >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vx as u8;
self.buffer[{ self.index += 1; self.index }] = (vy >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vy as u8;
self.buffer[{ self.index += 1; self.index }] = (vz >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vz as u8;
self.index += 1;
Ok(())
}
pub fn add_barometric_pressure(&mut self, channel: u8, hpa: f32) -> Result<(), Error> {
if self.index + LPP_BAROMETRIC_PRESSURE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let pressure = (hpa * 10.0) as u16;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_BAROMETRIC_PRESSURE;
self.buffer[{ self.index += 1; self.index }] = (pressure >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = pressure as u8;
self.index += 1;
Ok(())
}
pub fn add_voltage(&mut self, channel: u8, voltage: f32) -> Result<(), Error> {
if self.index + LPP_VOLTAGE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
if voltage * 100.0 > u16::MAX as f32 {
return Err(Error::OutOfRange);
}
let voltage: u16 = (voltage * 100.0) as u16;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_VOLTAGE;
self.buffer[{ self.index += 1; self.index }] = (voltage >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = voltage as u8;
self.index += 1;
Ok(())
}
pub fn add_current(&mut self, channel: u8, amperage: f32) -> Result<(), Error> {
if self.index + LPP_CURRENT_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
if amperage * 1000.0 > u16::MAX as f32 {
return Err(Error::OutOfRange);
}
let amperage: u16 = (amperage * 1000.0) as u16;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_CURRENT;
self.buffer[{ self.index += 1; self.index }] = (amperage >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = amperage as u8;
self.index += 1;
Ok(())
}
pub fn add_frequency(&mut self, channel: u8, frequency: u32) -> Result<(), Error> {
if self.index + LPP_FREQUENCY_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let frequency_bytes = frequency.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_FREQUENCY;
self.buffer[{ self.index += 1; self.index }] = frequency_bytes[0];
self.buffer[{ self.index += 1; self.index }] = frequency_bytes[1];
self.buffer[{ self.index += 1; self.index }] = frequency_bytes[2];
self.buffer[{ self.index += 1; self.index }] = frequency_bytes[3];
self.index += 1;
Ok(())
}
pub fn add_percentage(&mut self, channel: u8, percentage: u8) -> Result<(), Error> {
if self.index + LPP_PERCENTAGE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_PERCENTAGE;
self.buffer[{ self.index += 1; self.index }] = percentage;
self.index += 1;
Ok(())
}
pub fn add_altitude(&mut self, channel: u8, altitude: i16) -> Result<(), Error> {
if self.index + LPP_ALTITUDE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_ALTITUDE;
self.buffer[{ self.index += 1; self.index }] = (altitude >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = altitude as u8;
self.index += 1;
Ok(())
}
pub fn add_power(&mut self, channel: u8, power: u16) -> Result<(), Error> {
if self.index + LPP_POWER_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_POWER;
self.buffer[{ self.index += 1; self.index }] = (power >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = power as u8;
self.index += 1;
Ok(())
}
pub fn add_distance(&mut self, channel: u8, distance: u32) -> Result<(), Error> {
if self.index + LPP_DISTANCE_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let distance_bytes = distance.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_DISTANCE;
self.buffer[{ self.index += 1; self.index }] = distance_bytes[0];
self.buffer[{ self.index += 1; self.index }] = distance_bytes[1];
self.buffer[{ self.index += 1; self.index }] = distance_bytes[2];
self.buffer[{ self.index += 1; self.index }] = distance_bytes[3];
self.index += 1;
Ok(())
}
pub fn add_energy(&mut self, channel: u8, energy: u32) -> Result<(), Error> {
if self.index + LPP_ENERGY_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let energy_bytes = energy.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_ENERGY;
self.buffer[{ self.index += 1; self.index }] = energy_bytes[0];
self.buffer[{ self.index += 1; self.index }] = energy_bytes[1];
self.buffer[{ self.index += 1; self.index }] = energy_bytes[2];
self.buffer[{ self.index += 1; self.index }] = energy_bytes[3];
self.index += 1;
Ok(())
}
pub fn add_direction(&mut self, channel: u8, direction: u16) -> Result<(), Error> {
if self.index + LPP_DIRECTION_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_DIRECTION;
self.buffer[{ self.index += 1; self.index }] = (direction >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = direction as u8;
self.index += 1;
Ok(())
}
pub fn add_unixtime(&mut self, channel: u8, unixtime: u32) -> Result<(), Error> {
if self.index + LPP_UNIXTIME_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let unixtime_bytes = unixtime.to_be_bytes();
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_UNIXTIME;
self.buffer[{ self.index += 1; self.index }] = unixtime_bytes[0];
self.buffer[{ self.index += 1; self.index }] = unixtime_bytes[1];
self.buffer[{ self.index += 1; self.index }] = unixtime_bytes[2];
self.buffer[{ self.index += 1; self.index }] = unixtime_bytes[3];
self.index += 1;
Ok(())
}
pub fn add_gyrometer(&mut self, channel: u8, x: f32, y: f32, z: f32) -> Result<(), Error> {
if self.index + LPP_GYROMETER_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
let vx: u16 = (x * 100.0) as u16;
let vy: u16 = (y * 100.0) as u16;
let vz: u16 = (z * 100.0) as u16;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_GYROMETER;
self.buffer[{ self.index += 1; self.index }] = (vx >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vx as u8;
self.buffer[{ self.index += 1; self.index }] = (vy >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vy as u8;
self.buffer[{ self.index += 1; self.index }] = (vz >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vz as u8;
self.index += 1;
Ok(())
}
pub fn add_gps(&mut self, channel: u8, latitude: f32, longitude: f32, meters: f32) -> Result<(), Error> {
if self.index + LPP_GPS_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
if !(-90.0..=90.0).contains(&latitude) {
return Err(Error::OutOfRange);
}
if !(-180.0..= 180.0).contains(&longitude) {
return Err(Error::OutOfRange);
}
let vx: i32 = (latitude * 10000.0) as i32;
let vy: i32 = (longitude * 10000.0) as i32;
let vz: i32 = (meters * 100.0) as i32;
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_GPS;
self.buffer[{ self.index += 1; self.index }] = (vx >> 16) as u8;
self.buffer[{ self.index += 1; self.index }] = (vx >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vx as u8;
self.buffer[{ self.index += 1; self.index }] = (vy >> 16) as u8;
self.buffer[{ self.index += 1; self.index }] = (vy >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vy as u8;
self.buffer[{ self.index += 1; self.index }] = (vz >> 16) as u8;
self.buffer[{ self.index += 1; self.index }] = (vz >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = vz as u8;
self.index += 1;
Ok(())
}
pub fn add_switch(&mut self, channel: u8, value: bool) -> Result<(), Error> {
if self.index + LPP_SWITCH_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_SWITCH;
self.buffer[{ self.index += 1; self.index }] = if value { 1 } else { 0 };
self.index += 1;
Ok(())
}
pub fn add_concentration(&mut self, channel: u8, concentration: u16) -> Result<(), Error> {
if self.index + LPP_CONCENTRATION_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_CONCENTRATION;
self.buffer[{ self.index += 1; self.index }] = (concentration >> 8) as u8;
self.buffer[{ self.index += 1; self.index }] = concentration as u8;
self.index += 1;
Ok(())
}
pub fn add_color(&mut self, channel: u8, red: u8, green: u8, blue: u8) -> Result<(), Error> {
if self.index + LPP_COLOR_SIZE > self.buffer.len() {
return Err(Error::InsufficientMemory);
}
self.buffer[self.index] = channel;
self.buffer[{ self.index += 1; self.index }] = LPP_COLOR;
self.buffer[{ self.index += 1; self.index }] = red;
self.buffer[{ self.index += 1; self.index }] = green;
self.buffer[{ self.index += 1; self.index }] = blue;
self.index += 1;
Ok(())
}
}
impl<'a> IntoIterator for CayenneLPP<'a> {
type Item = Result<CayenneLPPScalar, Error>;
type IntoIter = CayenneLPPIntoFailableIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
CayenneLPPIntoFailableIterator {
lpp: self,
index: 0
}
}
}