Making music with Yun: how to transfer high rate data to linino ?

Hi there

I am a math teacher (not computer sciences ! :slight_smile: ) and we would like to build a music instrument (measure data & sonify it... for the time being we are trying using Yun movements).

I am reading the mpu6050 accel/gyro data on the Arduino 32u4 (sketch side) at 200 Hz and I am struggling to transfer it to the linino side to process it and send it in OSC via wifi (using pyOSC).

In a first approach, I was doing this exclusively on the sketch side (including the OSC part) but it was desperately slow. Inspired by ShapeShifter, I tried to use serial1 to transfer the data on the linino side to format/send the OSC message directly in the linino.

Here is how I tried:

Arduino sketch side (sending the serial1 messages at the end)

#include <Process.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps_V6_12.h"

// make ip and port constants
#define IP_ADR "192.168.1.181"
#define PORT 4560

#define INTERRUPT_PIN 3                                               // use pin 15 on ESP8266

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu;

#define OUTPUT_READABLE_WORLDACCEL
//#define OUTPUT_TEAPOT

// MPU control/status vars
uint8_t ratediv = 4;    // set the sample rate to 100 Hz
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 gy;         // [x, y, z]            gyro sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

// ================================================================
// ===               INTERRUPT DETECTION ROUTINE                ===
// ================================================================

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}

// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================

void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    Serial.begin(115200);
    while (!Serial); // wait for Leonardo enumeration, others continue immediately

    // initialize serial1 communication
    Serial1.begin(115200);
    
    [... varied MPU init [...]

}

// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================

void loop() {
    // if programming failed, don't try to do anything
    if (!dmpReady) return;

    // wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) {
        if (mpuInterrupt && fifoCount < packetSize) {
          // try to get out of the infinite loop 
          fifoCount = mpu.getFIFOCount();
        }  
        // other program behavior stuff here
    }

    // reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();

    // get current FIFO count
    fifoCount = mpu.getFIFOCount();

    // check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & (0x01 << MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        fifoCount = mpu.getFIFOCount();
        Serial.println(F("FIFO overflow!"));

    // otherwise, check for DMP data ready interrupt (this should happen frequently)
    } else if (mpuIntStatus & (0x01 << MPU6050_INTERRUPT_DMP_INT_BIT)) {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

        // read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;

        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetEuler(euler, &q);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        mpu.dmpGetAccel(&aa, fifoBuffer);
        mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
        mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);   

        String acc = String(aaWorld.x) + "," + String(aaWorld.y) + "," + String(aaWorld.z) + "\n";
        Serial1.print(acc);
        delay(2);
        
        String gyr = String(int(ypr[0]*180/PI)) + "," + String(int(ypr[1]*180/PI)) + "," + String(int(ypr[2]*180/PI)) + ",END\n";
        Serial1.print(gyr);
        delay(2);
    }
}

Linino python OSCsender.py script:

#!/usr/bin/python -u

import OSC
import serial
import re
				
# Setup the client connection
host = "192.168.1.181"
port = 4560 
adr  = "/yunMusic"
				
# initiate udp client and connect
client = OSC.OSCClient()
client.connect( (host, port) ) 

# instantiate a serial1 object
serialobj = serial.Serial('/dev/ttyATH0', 115200, timeout = 1)

# initialize an OSC message object, set adr
msg = OSC.OSCMessage(adr) 

while True:
	if serialobj.inWaiting() >= 0:
		# Read a new line from the serial1 and split into values
		values = serialobj.readline().decode("ascii").split(",")
		for value in values:
			vals = re.findall('-?\d+\.?\d*',value)
			if "END" not in value:
				if len(vals) > 0:
					for val in vals:
						msg.append(int(val))                   # just append the values
			else:
				i = value.find("END")	
				if i > 0: 
					if len(vals) > 0:
						for val in vals:
							msg.append(int(val))		# append the values before
					client.send(msg)				# Send the OSC message after
					msg = OSC.OSCMessage(adr) 
				else:
					client.send(msg)				# Send the OSC message before
					msg = OSC.OSCMessage(adr) 			# append the values after
					if len(vals) > 0:
						for val in vals:
							msg.append(int(val))

I still have to launch manually the python script for it, even if I included in my /etc/rc.local:

python /root/OSCtoPD/OSCsender.py

And when it works (sends OSC data on the network), it does not read and format the data as I would like. It should send at the adress "/yunMusic" something formatted like [accx, accy, accz, gyrx, gyry, gyrz] but it is often splited into [accx, accy, accz] and [gyrx, gyry, gyrz] or on the contrary concatenates several messages [accx, accy, accz, gyrx, gyry, gyrz, accx, accy, accz, gyrx, gyry, gyrz, accx, accy, accz, gyrx, gyry, gyrz, ....], or do nothing (not any OSC messages sent), or simply crashes with a posix error (multiple attempts to access to the serial which says it ready to be read and returning nothing).

How should I transfer/format these data from 32u4 to linino and how should I decode them in the python script ? (serial1.write() instead of serial1.print() ?)
Should I simply use another Arduino with a wifi shield to get an easier life ?

Any help would be greatly appreciated, this is for a project at school and I am a bit stuck.

Best,
Jeff

Hi there,
I had just forgotten to disable the bridge (comment out ::askconsole:/usr/libexec/login.sh in /etc/inittab) :slight_smile:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.