pub mod ov2640;
pub mod ov5640;
use rusty_esp_core::error::{Error, Result};
use rusty_esp_core::frame::{Geometry, PixelFormat};
use crate::sccb::RegWidth;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Register {
pub addr: u16,
pub value: u8,
}
impl Register {
#[must_use]
pub const fn new(addr: u16, value: u8) -> Self {
Register { addr, value }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegOp {
Write {
addr: u16,
value: u8,
},
DelayMs(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SensorId {
Ov9650,
Ov7725,
Ov2640,
Ov3660,
Ov5640,
Ov7670,
Nt99141,
Gc2145,
Gc032a,
Gc0308,
Bf3005,
Bf20a6,
Sc101iot,
Sc030iot,
Sc031gs,
MegaCcm,
Hm1055,
Hm0360,
}
impl SensorId {
#[must_use]
pub const fn pid(self) -> u16 {
match self {
SensorId::Ov9650 => 0x96,
SensorId::Ov7725 => 0x77,
SensorId::Ov2640 => 0x26,
SensorId::Ov3660 => 0x3660,
SensorId::Ov5640 => 0x5640,
SensorId::Ov7670 => 0x76,
SensorId::Nt99141 => 0x1410,
SensorId::Gc2145 => 0x2145,
SensorId::Gc032a => 0x232A,
SensorId::Gc0308 => 0x9B,
SensorId::Bf3005 => 0x30,
SensorId::Bf20a6 => 0x20A6,
SensorId::Sc101iot => 0xDA4A,
SensorId::Sc030iot => 0x9A46,
SensorId::Sc031gs => 0x0031,
SensorId::MegaCcm => 0x039E,
SensorId::Hm1055 => 0x0955,
SensorId::Hm0360 => 0x0360,
}
}
#[must_use]
pub fn from_pid(pid: u16) -> Option<SensorId> {
ALL_IDS.iter().copied().find(|s| s.pid() == pid)
}
}
const ALL_IDS: &[SensorId] = &[
SensorId::Ov9650,
SensorId::Ov7725,
SensorId::Ov2640,
SensorId::Ov3660,
SensorId::Ov5640,
SensorId::Ov7670,
SensorId::Nt99141,
SensorId::Gc2145,
SensorId::Gc032a,
SensorId::Gc0308,
SensorId::Bf3005,
SensorId::Bf20a6,
SensorId::Sc101iot,
SensorId::Sc030iot,
SensorId::Sc031gs,
SensorId::MegaCcm,
SensorId::Hm1055,
SensorId::Hm0360,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SensorDesc {
pub id: SensorId,
pub name: &'static str,
pub sccb_addr: u8,
pub reg_width: RegWidth,
pub pid_reg: u16,
pub pid_len: u8,
pub pid: u16,
pub max_width: u32,
pub max_height: u32,
}
impl SensorDesc {
pub fn max_geometry(&self, format: PixelFormat) -> Result<Geometry> {
Geometry::new(self.max_width, self.max_height, format)
}
}
pub const SENSORS: &[SensorDesc] = &[
SensorDesc {
id: SensorId::Ov2640,
name: "OV2640",
sccb_addr: ov2640::SCCB_ADDR,
reg_width: RegWidth::U8,
pid_reg: 0x0A,
pid_len: 1,
pid: SensorId::Ov2640.pid(),
max_width: 1600,
max_height: 1200,
},
SensorDesc {
id: SensorId::Ov5640,
name: "OV5640",
sccb_addr: ov5640::SCCB_ADDR,
reg_width: RegWidth::U16,
pid_reg: 0x300A,
pid_len: 2,
pid: SensorId::Ov5640.pid(),
max_width: 2560,
max_height: 1920,
},
SensorDesc {
id: SensorId::Ov3660,
name: "OV3660",
sccb_addr: 0x3C,
reg_width: RegWidth::U16,
pid_reg: 0x300A,
pid_len: 2,
pid: SensorId::Ov3660.pid(),
max_width: 2048,
max_height: 1536,
},
SensorDesc {
id: SensorId::Ov7670,
name: "OV7670",
sccb_addr: 0x21,
reg_width: RegWidth::U8,
pid_reg: 0x0A,
pid_len: 1,
pid: SensorId::Ov7670.pid(),
max_width: 640,
max_height: 480,
},
];
#[must_use]
pub fn describe(id: SensorId) -> Option<&'static SensorDesc> {
SENSORS.iter().find(|d| d.id == id)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FrameSize {
S96x96,
Qqvga,
S128x128,
Qcif,
Hqvga,
S240x240,
Qvga,
S320x320,
Cif,
Hvga,
Vga,
Svga,
Xga,
Hd,
Sxga,
Uxga,
Fhd,
Qxga,
Qhd,
Qsxga,
S5mp,
}
impl FrameSize {
#[must_use]
pub const fn dimensions(self) -> (u32, u32) {
match self {
FrameSize::S96x96 => (96, 96),
FrameSize::Qqvga => (160, 120),
FrameSize::S128x128 => (128, 128),
FrameSize::Qcif => (176, 144),
FrameSize::Hqvga => (240, 176),
FrameSize::S240x240 => (240, 240),
FrameSize::Qvga => (320, 240),
FrameSize::S320x320 => (320, 320),
FrameSize::Cif => (400, 296),
FrameSize::Hvga => (480, 320),
FrameSize::Vga => (640, 480),
FrameSize::Svga => (800, 600),
FrameSize::Xga => (1024, 768),
FrameSize::Hd => (1280, 720),
FrameSize::Sxga => (1280, 1024),
FrameSize::Uxga => (1600, 1200),
FrameSize::Fhd => (1920, 1080),
FrameSize::Qxga => (2048, 1536),
FrameSize::Qhd => (2560, 1440),
FrameSize::Qsxga => (2560, 1920),
FrameSize::S5mp => (2592, 1944),
}
}
pub fn geometry(self, format: PixelFormat) -> Result<Geometry> {
let (w, h) = self.dimensions();
Geometry::new(w, h, format)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Mode {
pub geometry: Geometry,
pub fps: u8,
pub jpeg_quality: u8,
pub flip: bool,
pub mirror: bool,
}
impl Mode {
pub fn jpeg(size: FrameSize) -> Result<Self> {
Ok(Mode {
geometry: size.geometry(PixelFormat::Jpeg)?,
fps: 0,
jpeg_quality: 12,
flip: false,
mirror: false,
})
}
pub fn raw(size: FrameSize, format: PixelFormat) -> Result<Self> {
match format {
PixelFormat::Rgb565 | PixelFormat::Yuyv422 | PixelFormat::Gray8 => {}
_ => return Err(Error::Unsupported),
}
Ok(Mode {
geometry: size.geometry(format)?,
fps: 0,
jpeg_quality: 12,
flip: false,
mirror: false,
})
}
pub fn check(&self, desc: &SensorDesc) -> Result<()> {
if self.geometry.width > desc.max_width || self.geometry.height > desc.max_height {
return Err(Error::Unsupported);
}
if self.geometry.format == PixelFormat::Jpeg && !(1..=63).contains(&self.jpeg_quality) {
return Err(Error::InvalidFormat);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pids_round_trip_and_descriptors_agree() {
for id in ALL_IDS {
assert_eq!(SensorId::from_pid(id.pid()), Some(*id));
}
for d in SENSORS {
assert_eq!(d.pid, d.id.pid());
assert!(d.pid_len == 1 || d.pid_len == 2);
assert!(d.max_width > 0 && d.max_height > 0);
assert_eq!(describe(d.id), Some(d));
}
assert!(describe(SensorId::Hm0360).is_none());
}
#[test]
fn tables_are_non_empty_and_sane() {
assert!(ov2640::SETTINGS_CIF.len() > 100);
assert!(ov2640::SETTINGS_JPEG3.len() > 5);
assert!(ov2640::SETTINGS_YUV422.len() > 3);
assert!(ov2640::SETTINGS_RGB565.len() > 3);
for t in [
ov2640::SETTINGS_CIF,
ov2640::SETTINGS_TO_SVGA,
ov2640::SETTINGS_TO_UXGA,
] {
assert!(
matches!(
t[0],
RegOp::Write {
addr: 0xFF,
value: 0 | 1
}
),
"{:?}",
t[0]
);
}
assert!(ov5640::DEFAULT_REGS.len() > 100);
assert!(
ov5640::FMT_JPEG
.iter()
.all(|op| matches!(op, RegOp::Write { .. }))
);
assert!(matches!(ov5640::DEFAULT_REGS[0], RegOp::Write { addr, .. } if addr >= 0x3000));
}
#[test]
fn modes_and_sizes() {
let (w, h) = FrameSize::Qvga.dimensions();
assert_eq!((w, h), (320, 240));
let m = Mode::jpeg(FrameSize::Uxga).unwrap();
assert!(m.check(describe(SensorId::Ov2640).unwrap()).is_ok());
assert_eq!(
Mode::jpeg(FrameSize::Qxga)
.unwrap()
.check(describe(SensorId::Ov2640).unwrap()),
Err(Error::Unsupported)
);
let mut bad = m;
bad.jpeg_quality = 0;
assert_eq!(
bad.check(describe(SensorId::Ov2640).unwrap()),
Err(Error::InvalidFormat)
);
}
}