Software/Arduino Core/Advanced

Writing a Camera Driver

The ImageSensor interface a camera library implements, registration, the format contract, and the CustomSensor example.

advanced2 min read

Each camera chip supported by the core is an ImageSensor subclass in its own library. The OV5640 and VD66GY libraries are the two implementations. A new sensor is added by writing one class; no core changes are needed unless the sensor outputs a pixel format the core does not handle.

Interface

cpp
class MySensor : public ImageSensor {
public:
  MySensor() { registerAsActive(1u); }       // 1 = physical CSI lanes, 2 = inverted

  int init(CameraResolutionN6 res, CameraFpsN6 fps, CameraMirrorFlipN6 flip,
           uint16_t &out_w, uint16_t &out_h) override {
    // reset, chip ID check, register tables, orientation, stream on, over I2C
    out_w = 640; out_h = 480;
    return 0;                                // 0 on success, negative on failure
  }

  uint32_t chipId() override { return 0x1234u; }

  void getFormat(SensorFormat &out) override {
    out.pixel_format = N6_PIXFMT_YUV422_YUYV;
    out.color_space  = N6_COLORSPACE_BT601;
  }

  void streamOn()  override {}
  void streamOff() override {}
  const char *name() override { return "MySensor"; }
};

MySensor camera;
MethodFunction
ConstructorCalls registerAsActive(lanes). Runs at static initialisation, before the HAL is ready, and must not touch hardware
init()Brings the sensor up at the requested mode, frame rate and orientation. Reports the achieved dimensions. Returns 0 or a negative error
maxFps(res)The native maximum frame rate for a mode. Defaults to the shared table; overridden by sensors with different limits
chipId()The expected chip ID, used in the console diagnostic when init() fails
getFormat()The pixel format and colour space the sensor outputs
streamOn(), streamOff()Start and stop pixel output
name()The name printed in diagnostics

Optional methods cover autofocus, strobe, exposure, gain and white balance, with controlCaps() reporting which are implemented.

Registration

registerAsActive() records the object as the sensor Vision drives and publishes its CSI data lane mapping. The lane argument is a property of the carrier: 1 for the standard wiring, 2 for inverted lanes. Vision.begin() later calls init(), getFormat() and streamOn() on the registered object.

A sketch declares exactly one sensor object. Vision.setSensor() exists for choosing between several declared sensors at run time and is rarely needed.

Format contract

getFormat() drives the whole DCMIPP pipeline: the CSI data type, the packer, the colour space conversion and the JPEG encoder configuration. Three formats are defined:

FormatDescriptionUsed by
N6_PIXFMT_YUV422_YUYVProcessed YUV 4:2:2OV5640
N6_PIXFMT_RGB565Processed RGBReserved for future sensors
N6_PIXFMT_RAW8Raw Bayer or monochrome, demosaiced by the DCMIPP ISP. bayer_order selects the patternVD66GY

A sensor with a format not in this list requires a case added to n6_apply_sensor_format() in cores/NeuroN6/NeuroN6.c.

I2C

Sensor control runs over I2C1, shared with the on-board sensors. A driver that calls the HAL directly brackets each transaction with n6_i2c_lock() and n6_i2c_unlock(). See I2C with Wire.

Failure behaviour

If init() returns a negative value, Vision.begin() prints a console line naming the sensor and its expected chip ID, and Vision.run() returns early on every call rather than blocking in inference. A sketch with a missing or miswired camera therefore degrades to a diagnostic.

References

The CustomSensor example, under File > Examples > OV5640, is a compilable skeleton. libraries/OV5640/src/OV5640_Sensor.cpp is a complete implementation with autofocus, strobe and capture controls. libraries/VD66GY/src/VD66GY_Sensor.cpp shows a raw Bayer sensor with an ISP.