Photodiode detextion on a rotating disk


I am working on a project where I would like to use a photodetector or multiple detectors to read light signals on a rotating disk (6000rpm). Currently I am using the SDfat library in combination with a teensy 4.1 and am able to read at 25kHz.

The disk will have 22 channels (symmetrical) that need to be read. The diode(s) could either be below or above the disk.

I have tinkered a bit but I am a novice when it comes to this type of projects. I have a setup where I have two diodes connected to an op-amp (TL072) and a second op-amp (opa350pa) as a comparator. The amplification is working as intended (transimpedance amplification). The channels have two points of light, and I am interested in the absolute difference of the light intensity.

What I need help with now is a way to get these values, currently I get a lot of data points and can't figure out if the data is from a channel or the empty space on the disk. I thought about making a peak finder code but that will only give me the channels with light, and I am interested in the value of the channel itself, so I need to know if there is light there or not.

I hope that I have included all the information needed,

No. Please post a wiring diagram and code.

I tried uploading the code but was not allowed since I was a new user.

#include "SdFat.h"
#include "RingBuf.h"

// Use Teensy SDIO
#define SD_CONFIG  SdioConfig(FIFO_SDIO)

// Interval between points for 25 ksps.

// Size to log 10 byte lines at 25 kHz for more than ten minutes.
#define LOG_FILE_SIZE 10*25000*0.05  // 150,000,000 bytes.

// Space to hold more than 800 ms of data for 10 byte lines at 25 ksps.
#define RING_BUF_CAPACITY 400*512
#define LOG_FILENAME "laserSample.csv"

SdFs sd;
FsFile file;

// RingBuf for File type FsFile.
RingBuf<FsFile, RING_BUF_CAPACITY> rb;

void logData() {
  // Initialize the SD.
  if (!sd.begin(SD_CONFIG)) {
  // Open or create file - truncate existing file.
  if (!, O_RDWR | O_CREAT | O_TRUNC)) {
    Serial.println("open failed\n");
  // File must be pre-allocated to avoid huge
  // delays searching for free clusters.
  if (!file.preAllocate(LOG_FILE_SIZE)) {
     Serial.println("preAllocate failed\n");
  // initialize the RingBuf.
  Serial.println("Type any character to stop");

  // Max RingBuf used bytes. Useful to understand RingBuf overrun.
  size_t maxUsed = 0;

  // Min spare micros in loop.
  int32_t minSpareMicros = INT32_MAX;

  // Start time.
  uint32_t logTime = micros();
  // Log data until Serial input or file full.
  while (!Serial.available()) {
    // Amount of data in ringBuf.
    size_t n = rb.bytesUsed();
    if ((n + file.curPosition()) > (LOG_FILE_SIZE - 20)) {
      Serial.println("File full - quiting.");
    if (n > maxUsed) {
      maxUsed = n;
    if (n >= 512 && !file.isBusy()) {
      // Not busy only allows one sector before possible busy wait.
      // Write one sector from RingBuf to file.
      if (512 != rb.writeOut(512)) {
        Serial.println("writeOut failed");
    // Time for next point.
    logTime += LOG_INTERVAL_USEC;
    int32_t spareMicros = logTime - micros();
    if (spareMicros < minSpareMicros) {
      minSpareMicros = spareMicros;
    if (spareMicros <= 0) {
      Serial.print("Rate too fast ");
    // Wait until time to log data.
    while (micros() < logTime) {}

    // Read ADC0 - about 17 usec on Teensy 4, Teensy 3.6 is faster.
    uint16_t adc = analogRead(0);
    // Print spareMicros into the RingBuf as test data.
    // Print adc into RingBuf.
    if (rb.getWriteError()) {
      // Error caused by too few free bytes in RingBuf.
  // Write any RingBuf data to file.
  // Print first twenty lines of file.
  for (uint8_t n = 0; n < 20 && file.available();) {
    int c =;
    if (c < 0) {
    if (c == '\n') n++;
  Serial.print("fileSize: ");
  Serial.print("maxBytesUsed: ");
  //Serial.print("minSpareMicros: ");
 // Serial.println(minSpareMicros);
void clearSerialInput() {
  for (uint32_t m = micros(); micros() - m < 10000;) {
    if ( >= 0) {
      m = micros();
void setup() {
  while (!Serial) {};

void loop() {
  Serial.println("Type any character to start");
  while (!Serial.available()) {};

I can't attach any diagrams but I am basically running this. Redirect Notice
The op-amp is TL072, Rf is 300MOhm and Cf is 47uF. The circuit is run by the teensy (5V).

KiCad schematic with one diode

You can read about magnetic disk head position strategies.

Without more detailed description I have no idea of your planned device. Fixed or movable heads? Opaque or transparent disk? Data and clock encoding? ...

The disk is opaque acrylic. The channels are transparent and have a solution that emits light when excited by a laser. The channel width is 5mm. I need to read the light intensity (or lack of) on all channels, and also know which channel is which.

What "channels"? Tracks?

The schematic seems to show the diode connected the wrong way around.

Typical circuit, but not one convenient for Arduino because the output voltage is negative:

For positive voltage output, use photovoltaic mode

or negative bias:

Yes, the tracks on which I would like to measure the light intensity

Thanks for the schematic! I am using this set-up but DC, so no -5V and it is working, but what I am having issues with is how to find the position of the channels(tracks) on the disk, so that I can know which value of light intensity is which.

Count the tracks while moving the head.

Sorry but I don't really understand. What head? the rotating axis? and how do I count them? I want it to be automated, if possible.

The TL072 is not capable of rail-rail operation so may not give useful signals.

You will find an example of photodiode detection on my tutorial here

provide a picture of your device/engine.

Why? Are you measuring speed of rotation or are you attempting to determine the degrees of rotation for accurate positioning or what ?

What pattern have you on the disk? something like one of these :


Pssst - that is a secret :laughing:


Thank you, I will change to one of the recommended op-amps.

The pattern looks like this but symmetrical. The tracks are hollow and will have solution that will emit light, I am interested in what that light is in each channel (solved with a photodetector) and what light is corresponding to what channel. Right now I get around 12k Values with values from 0 to 3V, and I can't determine what is what.

I'm sorry to be dim, but what do you mean by a "channel"?

The channels on the disk, or tracks. The picture above with a disk and lines, the lines represent the channels. In these channels there is a solution that emits light.

1 Like

Got it, thank you.