Serial Acquisition#

[1]:
import os
import sys
sys.path.insert(0, os.path.realpath('../'))
[2]:
import LDAQ
[3]:
# Create serial acquisition source:
acq_serial = LDAQ.serial_communication.SerialAcquisition(port="COM3", baudrate=250000,
                                                acquisition_name="arduino",
                                                byte_sequence=(("int16", 2), ),
                                                start_bytes=  b"\xfa\xfb",
                                                end_bytes= b"\n",
                                                sample_rate=500,
                                                channel_names=["channel 1", "channel 2"])

# Here the arduino sends out bytes over serial where a line would look like this:
# b"\xfa\xfb\x00\x01\x00\x02\n
# Explanation:
# b"\xfa\xfb" are the start bytes
# b"\n" is the end byte
# b"\x00\x01" is the first  sample (int16)
# b"\x00\x02" is the second sample (int16)
[4]:
# Each acquisition source can also be run by itself:
acq_serial.run_acquisition(5.0)   # run for 2 seconds
acq_serial.get_measurement_dict() # get the measurement dictionary
[4]:
{'time': array([0.000e+00, 2.000e-03, 4.000e-03, ..., 4.994e+00, 4.996e+00,
        4.998e+00]),
 'channel_names': ['channel 1', 'channel 2'],
 'data': array([[512., 906.],
        [512., 944.],
        [512., 975.],
        ...,
        [512., 758.],
        [512., 812.],
        [512., 862.]]),
 'sample_rate': 500}
[5]:
# create core object and add acquisition source:
ldaq = LDAQ.Core(acquisitions=[acq_serial])
# set trigger:
ldaq.set_trigger(
    source="arduino",
    channel="channel 2",
    level=100,
    duration=5.0)
# run acquisition:
ldaq.run()
+--------+-------------------------------------------------+
| HOTKEY |                   DESCRIPTION                   |
+--------+-------------------------------------------------+
|   s    | Start the measurement manually (ignore trigger) |
+--------+-------------------------------------------------+
|   q    |              Stop the measurement               |
+--------+-------------------------------------------------+

Waiting for trigger...
triggered.
        Recording...Measurement finished.
sys:1: ResourceWarning: Unclosed socket <zmq.Socket(zmq.PUSH) at 0x19d60157ee0>
ResourceWarning: Enable tracemalloc to get the object allocation traceback
[6]:
# Retrieve the measurement data:
measurement = ldaq.get_measurement_dict()
measurement
[6]:
{'arduino': {'time': array([0.000e+00, 2.000e-03, 4.000e-03, ..., 4.994e+00, 4.996e+00,
         4.998e+00]),
  'channel_names': ['channel 1', 'channel 2'],
  'data': array([[ 512.,  576.],
         [ 512.,  639.],
         [ 512.,  700.],
         ...,
         [-512.,  384.],
         [-512.,  447.],
         [ 512.,  512.]]),
  'sample_rate': 500}}

C code uploaded to the Arduino:#

#include <avr/interrupt.h>
#include <stdint.h>
#include <math.h>

const uint16_t sampleRate = 500; // Set the desired sample rate in Hz

// Signal generation parameters:
const float frequencySquareWave = 5; // Frequency of the square wave in Hz
const float frequencySinWave = 10;   // Frequency of the sinusoidal wave in Hz
const float amplitudeSinWave = 512;  // Amplitude of the sinusoidal wave
const float offsetSinWave = 512;     // Offset for the sinusoidal wave

// variables:
int16_t valSquareWave = 0;
int16_t valSinWave = 0;

// Other variables setup:
uint32_t sampleCounter = 0;

void setup() {
  Serial.begin(250000); // Begin serial, set baudrate
  setupTimerInterrupt();
}

void loop() {
  // Do nothing in the main loop, everything is done by the interrupt
}

void setupTimerInterrupt() {
  // Configure Timer1 for the desired sample rate
  cli(); // Disable interrupts
  TCCR1A = 0; // Clear control registers
  TCCR1B = 0;
  TCNT1 = 0; // Initialize counter value to 0
  uint32_t ocrValue = (F_CPU / 8 / sampleRate) - 1; // Calculate OCR1A value
  OCR1A = ocrValue; // Set compare match register for the desired sample rate
  TCCR1B |= (1 << WGM12); // Set CTC mode
  TCCR1B |= (1 << CS11); // Set 8 prescaler
  TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt
  sei(); // Enable interrupts
}

ISR(TIMER1_COMPA_vect) {
  // Timer interrupt routine
  float elapsedTime = (float)sampleCounter / sampleRate;
  sampleCounter++;

  // Generate square wave
  valSquareWave = (int16_t)(512 * (sin(2 * PI * frequencySquareWave * elapsedTime) >= 0 ? 1 : -1));
  // Generate sinusoidal wave
  valSinWave = (int16_t)(amplitudeSinWave * sin(2 * PI * frequencySinWave * elapsedTime) + offsetSinWave);

  Serial.write(0xfa); // starting byte 1
  Serial.write(0xfb); // starting byte 2
  send2Bytes(valSquareWave);
  send2Bytes(valSinWave);
  Serial.write('\n'); // ending byte
}

// Functions for sending data over serial:
void send4Bytes(uint32_t data) {
  Serial.write(data & 0xFF);
  Serial.write((data >> 8) & 0xFF);
  Serial.write((data >> 16) & 0xFF);
  Serial.write((data >> 24) & 0xFF);
}
void send2Bytes(int16_t data) {
  Serial.write(data & 0xFF);
  Serial.write((data >> 8) & 0xFF);
}
void send1Byte(int8_t data) {
  Serial.write(data & 0xFF);
}