Hi,
I have a sensor measuring the rotation of a DJ turntable. A magnet is attached to the center top of a vinyl record and a magnetometer encoder sensor detects its rotation.
I have the sensor calibrated. See this video of serial plot.
However I'm having trouble interpreting the readings when using a Python script that will control the playback of DJ software via synthesis of vinyl time code. My initial Python is only interpreting the reading as forward then back as is seen by watching the audio file in my DJ software playing forwards a little way then back again, repeat. See this video next video. You can't see the turntable but when the play head is moving the turntable is rotating. When the play head stops I've stopped the turntable. The timecode also shows as fairly stable in the meter (green/yellow circle). I'm so close !
I think it may has something to do with needing to converting the readings to Angular Velocity ?
Any ideas ?
ARDUINO:
#include "Adafruit_MLX90393.h"
Adafruit_MLX90393 sensor = Adafruit_MLX90393();
#define MLX90393_CS 10
void setup(void)
{
Serial.begin(115200);
/* Wait for serial on USB platforms. */
while (!Serial) {
delay(10);
}
Serial.println("Starting Adafruit MLX90393 Demo");
if (! sensor.begin_I2C(0x18, &Wire1)) { // hardware I2C mode, can pass in address & alt Wire
Serial.println("No sensor found ... check your wiring?");
while (1) { delay(10); }
}
Serial.println("Found a MLX90393 sensor");
sensor.setGain(MLX90393_GAIN_3X);
Serial.print("Gain set to: ");
switch (sensor.getGain()) {
case MLX90393_GAIN_1X: Serial.println("1 x"); break;
case MLX90393_GAIN_1_33X: Serial.println("1.33 x"); break;
case MLX90393_GAIN_1_67X: Serial.println("1.67 x"); break;
case MLX90393_GAIN_2X: Serial.println("2 x"); break;
case MLX90393_GAIN_2_5X: Serial.println("2.5 x"); break;
case MLX90393_GAIN_3X: Serial.println("3 x"); break;
case MLX90393_GAIN_4X: Serial.println("4 x"); break;
case MLX90393_GAIN_5X: Serial.println("5 x"); break;
}
sensor.setResolution(MLX90393_X, MLX90393_RES_17);
sensor.setResolution(MLX90393_Y, MLX90393_RES_17);
sensor.setResolution(MLX90393_Z, MLX90393_RES_16);
sensor.setOversampling(MLX90393_OSR_3);
sensor.setFilter(MLX90393_FILTER_5);
}
void loop(void) {
float x, y, z;
if (sensor.readData(&x, &y, &z)) {
Serial.println(z, 4); // Output only Z value with 4 decimal places
} else {
Serial.println("0"); // Output 0 if reading fails
}
delay(5);
}
PYTHON:
import serial
import sounddevice as sd
import numpy as np
import time
import threading
import sys
# Constants
N = 1800 # Number of timecode cycles per revolution
SAMPLERATE = 44100 # Audio sample rate in Hz
# Global variables for phase and speed estimation
phi_last = 0.0 # Last computed phase (radians)
t_last = time.time() # Time of last update (seconds)
omega_est = 0.0 # Estimated angular velocity (radians/second)
theta_last = None # Last received angle (radians)
# Set up serial port (adjust port name as needed)
try:
ser = serial.Serial('/dev/cu.usbmodem64E83369BAEC2', 115200, timeout=1)
except serial.SerialException:
print("Serial port not found. Check connection and port name.")
sys.exit(1)
# Audio callback function to generate timecode signal
def callback(outdata, frames, time, status):
global phi_last, t_last, omega_est
# Time when the first sample will be played
t_now = time.outputBufferDacTime
delta_t = t_now - t_last
# Current phase based on last known phase and estimated speed
phi = phi_last + N * omega_est * delta_t
# Phase increment per audio sample
delta_phi = N * omega_est / SAMPLERATE
# Generate stereo samples
for i in range(frames):
outdata[i, 0] = np.sin(phi) # Left channel
outdata[i, 1] = np.cos(phi) # Right channel (90° phase shift)
phi += delta_phi
# Serial reader thread function
def serial_reader():
global phi_last, t_last, omega_est, theta_last
while True:
line = ser.readline().decode().strip()
if line:
try:
theta = float(line)
t_now = time.time()
if theta_last is not None:
dt = t_now - t_last
# Avoid division by zero or unrealistic speeds
if dt > 0.001:
omega_est = (theta - theta_last) / dt
phi_last = N * theta
t_last = t_now
theta_last = theta
else:
# Initialize on first reading
theta_last = theta
t_last = t_now
except ValueError:
# Skip malformed data
continue
# Find Blackhole audio device
devices = sd.query_devices()
device_index = None
for dev in devices:
if 'External Headphones' in dev['name']:
device_index = dev['index']
break
if device_index is None:
print("External Headphones device not found.")
sys.exit(1)
# Start the serial reader thread
thread = threading.Thread(target=serial_reader)
thread.daemon = True # Thread exits when main program does
thread.start()
# Start audio stream
try:
with sd.OutputStream(samplerate=SAMPLERATE, channels=2, callback=callback, device=device_index):
print("Audio stream started. Sending timecode to External Headphones.")
# Keep the script running
while True:
time.sleep(1)
except Exception as e:
print(f"Error starting audio stream: {e}")
sys.exit(1)