Skip navigation
1 2 3 Previous Next

Enchanted Objects

45 Posts authored by: Jan Cumps Top Member
Jan Cumps

oh no - deadline passed

Posted by Jan Cumps Top Member Jun 26, 2015

Just too late to meet the timeline

 

I've been telling the story of the brothers Christian and Josef Steidinger here on the blog.

Christian is the one who left the family business and started the Perpetuum Ebner manufacture.

But to be honest, he's the one that didn't succeed. Brother George created the DUAL brand, and that company survived in its original form till the 80's.

 

The photos below are turntables that I have from DUAL. Some are brought back to specs. Others are Work In Progress.

I don't have a museum. Regularly I sell the ones that I have and buy new ones to play with.

 

 


 

{gallery} DUALs

Photo 26-06-15 21 50 58.jpg

DUAL 1010 and amp HS 32 (1960's): revised

Photo 26-06-15 21 53 22.jpg

DUAL 1019 (1960's): revised

Photo 26-06-15 21 53 47.jpg

DUAL 1218: donor for spare parts (1970's)

Photo 26-06-15 21 54 51.jpg

DUAL 1214 in console: wip (1970's)

Photo 26-06-15 21 55 41.jpg

DUAL 1218: WIP (1970's)

Photo 26-06-15 22 00 41.jpg

DUAL 510: revised (1970's)

 

 

 

 

The 1010 I've just purchased and fixed a week ago. That one has outside duty during sunny days. The others I pick and choose whenever I want, until I'm tired (or someone buys one).

 

{gallery} DUAL 510

Photo 16-06-15 09 23 24.jpg

DUAL 510: looks

Photo 16-06-15 09 23 45.jpg

DUAL 510: looks

Photo 16-06-15 09 23 58.jpg

DUAL 510: looks

Photo 16-06-15 09 24 09.jpg

DUAL 510: looks

Photo 16-06-15 09 24 22.jpg

DUAL 510: headshell

Photo 16-06-15 09 24 37.jpg

DUAL 510: cartridge and stylus

Photo 16-06-15 09 25 04.jpg

DUAL 510: tracking force and anti-skate

Photo 16-06-15 09 25 11.jpg

DUAL 510: speed control

Photo 16-06-15 09 25 53.jpg

DUAL 510: looks

 

 

If you're interested in turntable maintenance, I've got a Youtube channel dedicated to that subject.

20150622

 

 

It's the last week of the Enchanted Objects design challenge. The Enchanted Player plays, the blogs are blogged.


I'm closing with this video. Thank You All!


 

 

What's That Enchanted Player

 

A Perpetuum Ebner turntable from 1958 is controlled by modern technology. Microcontrollers measure the table speed and control the motor.

The audio is sampled and run through Fast Fourier Transform to turn it into chunks of the audio spectrum.

A light organ is hidden inside the belly of the turntable. It uses the spectrum info to flash Bass, Mids and Highs LEDs.

It's motorized, and only appears when there's music playing.

The audio spectrum is published to the cloud, where other applications can tap into it.

Uses Arduino UNO, Arduino Yún, Infineon Motor Shield, Arduino Proto Board

 

tabledrawing.jpg

 

The remote light organ plugs into the cloud to receive the spectrum info published by the player.

Because it uses WiFi, it can operate wireless if we desire to do so. Audio is again split in H/M/B.

This time the LEDs are driven by the RGB shield.

Uses extra Arduino Yún, Infineon RGB LED shield

 

sensorprinciple.jpg

 

The SAMA5D4 is plugged into my home network and also subscribes to the spectrum info. The Xplained Ultra board logs the received info to the console.

A great starting point to build a spectrum analyzer or a graphic spectrum display if screen is added.

All code and libraries are in C, cross-built on a Windows platform.

Uses the SAMA5D4 Xplained Ultra, GNU Toolchan and the Eclipse Paho client libraries.

 

What Happened

 

I've turned a defect 1950's treasure into a working turntable - without making a single scratch or permanent change.

All content from the challenge is used. Both Infineon boards, the Yún, the SAMA5D4, the UNO and the Arduino protoboard.

The servo motor - that didn't make it into the final design, has been featured in two technical blogs.

For the speed measurement and motor steering I've made a custom PCB. I added a single transistor pre-amp - an excuse to show that a very simple electronics design can be valuable.

 

large_i.png

I've made detailed drawings of the design concepts. I've also tried to use graphics to better explain processes and to lighten up the posts.

Photos - plenty of them, including more than 20 oscilloscope captures.

 

schema_kicad.png

 

Videos, plenty of those too. My youtube stats say that I've published 9 videos. That doesn't include the smaller ones that are embedded in the posts but don't tell a story on their own.

40+ blog posts.

There was a very deep dive into the SAMA5D4, and how to develop for it in C. That has resulted into 8 additional side posts on working with this board and reviews.

I've told the History of the Perpetuum Ebner company until World War 1.

dual 1010

 

Going Public

 

All my code and design files are published in the blogs, and where I used someone else's work, I've linked to that.

I've made my plans and progress (sometimes regress) public - including failures, mishaps and flukes.

Whenever I worked with the support teams of the devices, I've shared my experience - and links to the support portal items - to my story.

 

 

perpetuum ebner musical 1

 

And I enjoyed all of this a lot - and I have learned so much on the road.

If you want to check how good the turntable actually sounds, check out the video at the top of this post.

The soundbites you hear at the start and end are directly taken from this project; no tricks - no filtering.

 

You'll hear Eddy Romy telling Swedish Stories (in Dutch ) from this childhood record (Monopole 719 - Belgium, 1971)

enchanted objects enchanted player

 

 

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

 

 

Bye Bye

18 june 2015

 

As a dramatic closing exercise to the Enchanted Objects parade, I chose to try and build a C MQTT client for the SAMA5D4.

It turned out to be not dramatic at all. It was rather easy. Here's my log of the activities.

 

 

mqtt sama5d4 eclipse paho

 

My ToolSet

 

I'm cross-building the library and executable on a Windows machine.

I used Eclipse for DS-5 - Community Edition as my IDE, and used the built-in ARM GCC 4.x toolchain.

My telnet client is PuTTY, and WinSCP does the file transfer.

I used the GIT command line to retrieve the Paho client code.

The Paho MQTT client utility was my testbed.

 

I've made a how-to on using these tools as part of my blog series.

 

 

Building the MQTT Shared Library

 

The source code for the eclipse Paho libraries is available from github.

You can find the instructions on eclipse's MQTT C Client for Posix and Windows.

Because I'm using DS-5 for the build (and also because the makefile didn't work on my Windows PC), I've only followed the steps to download the code.

 

C:\Users\Jan\Documents\elektronica\atmel\mqtt_lib\paho_from_source>git clone http://git.eclipse.org/gitroot/paho/org.eclipse.paho.mq
tt.c.git
Cloning into 'org.eclipse.paho.mqtt.c'...
remote: Counting objects: 1719, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 1719 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1719/1719), 787.24 KiB | 31.00 KiB/s, done.
Resolving deltas: 100% (1177/1177), done.
Checking connectivity... done.









 

Then I created a new Shared Library project in DS-5. I've used the makefile source as guideline to check what files to compile and what command line options to pass to the toolchain.

 

 

I carefully studied the makefile to understand what files to include/exclude from build, and what settings to port from make to DS-5.

I'm building the async lib version without SSL, so I took over the settings from that lib's make commands.

 

blatant promo shot

 

I excluded the files listed in this variable from compilation, just like in the make script.

 

SOURCE_FILES_A = $(filter-out $(srcdir)/MQTTClient.c $(srcdir)/MQTTVersion.c $(srcdir)/SSLSocket.c, $(SOURCE_FILES))









 

 

I also checked where this particular makefile variable was used:

 

MQTTASYNC_INIT = MQTTAsync_init









 

and incorporated it in the linker flags:

 

Then I built the project. That gave me a compiled .so library.

 

Build the MQTT Subscriber App

 

I used the  async subscription example that's available in the same git extract. MQTTAsync_subscribe.c

 

I created a new project in DS-5, and added the source  to that project.

I took care that the async shared library was available to the linker:

 

I modified the source to connect to eclipse's mqtt sandbox, and to listen to my turntable's topic:

 

#define ADDRESS     "tcp://iot.eclipse.org"
#define CLIENTID    "1cfee8dd0afc_SAMA5D4"
#define TOPIC       "PerpetuumEbnerSpectrum"









 

Build, and done!

 

Load and Run

 

I deliberately didn't do all the steps to properly register the shared library.

 

I handled the part of setting the lib search path on the command line when invoking my executable.

There's a nice article on how to do a full-fledged install of a .so here: Shared Libraries.

 

I moved both the .so and the executable to root's home on the SAMA5D4 with WinSCP,

made both the program objects executable:


$ chmod +x Enchanted_Player_MQTT_Client









 

note to self: does an .so really need the exex flag set?

answer to self: no; explanation updated

 

and invoked the whole chebang using:

$ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./Enchanted_Player_MQTT_Client









 

Nothing more to say. Success. It just worked. My audio spectrum, sampled by the Yún inside my turntable, arrives nicely on the SAMA5D4.

 

mqttreceived_frompe.jpg

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

28 june 2015

My first bonus design is a remote light organ. I've built it with an Arduino Yún, the Infineon RGB Led Shield and a bunch of LEDs.

Part 1 was my log on the build, and trying out if the Arduino-to-Infineon-toLEDs works.

In part 2, I'm showing off the completed design.

 

paho mqtt enchanted_objects enchanted_player

 

It Didn't Work Just Like That

 

I had issues getting it to work with a 64 char payload. I have some candidate root causes for that, but it's hard to get to the bottom without debugger (I think it is my mechanism to use a char array as payload).

I spent some time riddling my code with println()s and stared at it for a long time, to no avail. So I took the easy way out.

I've reduced my frequency spectrum bucket array from 64 to 32 buckets. That's enough for my purpose - I want to drive a 16*2 LCD in Bonus 2, and 32 data points will be enough..

 

This video shows the action. Appreciate the huge delay that I have This is nowhere near to real time light effects.

The next design challenge I promise to spend more time in sound programming and performance. This time I only spent time in getting things working somehow.

 

 

 

 

Why don't you improve my code? It's all here.

I bet I'm off where I'm reverting the 64 bucket list to smaller on the server, and again when I'm reading that same bucket list on the client.

Go ahead and slam those bugs in my face. That's how I learn.

 

The light organ server code, firmware for the Yún that's built into the turntable

 

// includes for servo
#include <Servo.h>


// includes for light organ
#include <fix_fft.h>




// MQTT  includes start
#include <Bridge.h>
#include <SPI.h>
#include <YunClient.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>


#include <string.h>
// MQTT includes end








// servo declaration


Servo servo;


#define SERVO_PIN 9
#define SERVO_DELAY 25
#define SERVO_BASE 90
#define SERVO_TOP (SERVO_BASE + 61)


int servoPos = SERVO_BASE;
bool servoUp = false;






// light organ declaration


#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT
#define LOGM 7                 // Logaritmo en base 2 del número de muestras


#define BAJOS_MEDIOS 7         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35       // Nº de banda para el corte entre Medios y Agudos


#define BPIN  13                // Pin de salida Bajos
#define MPIN  12               // Pin de salida Medios
#define APIN  11               // Pin de salida Agudos
#define TIMERPIN 8




#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los lÃmites


char data[MUESTRAS];           // Array con los valores muestreados (parte real)
char im[MUESTRAS];             // Array con los valores muestreados (parte imaginaria)


unsigned char salida[MUESTRAS/2];  // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos; // Valores calculados para cada canal


byte  pasada,                            // nº de pasada para el cáculo de los lÃmites
      acumBajos,acumMedios,acumAgudos,   // acumuladores de veces que se supera el lÃmite
      limBajos,limMedios,limAgudos;      // lÃmites calculados para cada canal




// start MQTT declarations
YunClient c; // replace by a YunClient if running on a Yun
IPStack ipstack(c);
MQTT::Client<IPStack, Countdown> client = MQTT::Client<IPStack, Countdown>(ipstack);
char payLoadBuf[MUESTRAS/2] = {}; // I'm going to give one byte per range, so that I can choose to implement a graph if I want.
const char* send_topic = "PerpetuumEbnerSpectrum";
const char* _id = "1cfee8dd0afc_yun_syberia";
// end MQTT declarations






// both:
// decay is a decreasing counter that tells how long the servo will stay up after the last led flashed.
// reset to DECAY each time a led lights up
// looses one live each time no leds are active
#define DECAY 150U
unsigned int uDecay = 0U;




// servo functionality


void servoInit() {
    servoUp = false;
    servo.attach(SERVO_PIN);
    servoPos = SERVO_BASE;
    servo.write(SERVO_BASE);
    delay(1000);
}


void servoGoUp() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos < SERVO_TOP) {
    servoPos++;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGoDown() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos > SERVO_BASE) {
    servoPos--;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGo() {
  if (servoUp) {
    servoGoUp();
  } else {
    servoGoDown();
  }
}




// light organ fuctions


/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
    double muestrasMenosUno = (double(MUESTRAS) - 1.0);
  // Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
    for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
        double indiceMenosUno = double(i);
        double ratio = (indiceMenosUno / muestrasMenosUno);
        double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
  vData[i] *= factorPeso;
  vData[MUESTRAS - (i + 1)] *= factorPeso;
    }
}




void lightOrganSetup() {
    // Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
    // como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
    // Es decir podemos medir en teoria hasta unos 19KHz,
    // que para este proyecto sobra.
    bitWrite(ADCSRA,ADPS2,1);
    bitWrite(ADCSRA,ADPS1,0);
    bitWrite(ADCSRA,ADPS0,1);


    // Como la señal es muy baja,utilizamos la referencia interna
    // de 1.1 V en vez de la de defecto de 5 V.
    analogReference(INTERNAL);

    // Salidas para los canales de Bajos,Medios y Agudos
    pinMode(BPIN,OUTPUT);
    pinMode(MPIN,OUTPUT);
    pinMode(APIN,OUTPUT);


    // debug
    pinMode(TIMERPIN,OUTPUT);
    Serial.begin(9600);


    // Variables para el cálculo de los lÃmites
    pasada = 0;
    acumBajos = acumMedios = acumAgudos = 0;
    limBajos = limMedios = limAgudos = 50;

  }


void lightOrganGo() {
    // Realizamos el muestreo
//    Serial.println("Start sampling:");
    digitalWrite(TIMERPIN, HIGH);
    for( int i=0; i < MUESTRAS; i++) {
       data[i] = analogRead(0)/4 -128;  //Convertimos de 0..1024 a -128..127                            
       im[i] = 0;                       // parte imaginaria = 0                     
    }
    digitalWrite(TIMERPIN, LOW);

/* 
    {
    Serial.println(" samples\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");

    }
*/


    // Aplicamos la ventana de Hann
    aplicaVentana (data);


/*
    Serial.println(" window\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/
    // Calculamos la FFT
    fix_fft(data,im,LOGM,0);

    // Sólo nos interesan los valores absolutos, no las fases, asi que
    // calculamos el módulos de los vectores re*re + im*im.
    // Dado que los valores son pequeños utilizamos el cuadrado
    for (int i=0; i < MUESTRAS/2; i++){
       salida[i] = data[i] * data[i] + im[i] * im[i];
       // the cloud message contains half of the buckets, each pair of buckets is mediated
       if (i % 2) { // uneven
         payLoadBuf[i/2] = (salida[i] + salida [i-1])/2;
       }
    }
/*
    Serial.println(" result\n");
     for( int i=0; i < MUESTRAS/2; i++) {
      Serial.print(salida[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/

    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda
    bajos = 0;
    for (int i=2; i < BAJOS_MEDIOS; i++){
      bajos += salida[i];
    }
    bajos = bajos/2;

    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += salida[i];
    }
    medios = medios/2;

    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
      agudos += /*2**/(salida[i]);   // jc 20150604 undid because Yun ADC more sensitive or so // 20150601 doubled the highs sesitivity
    }
    agudos = agudos/2;

   // Calculamos si el canal correspondiente
   // supera le lÃmite para encenderlo
   int siBajos  =  bajos  > limBajos;
   int siMedios =  medios > limMedios;
   int siAgudos =  agudos > limAgudos;

   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);

   // Utilizamos las veces que se supera para
   // recalcular el lÃmite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;

   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
   }


   if (siBajos | siMedios /*| siAgudos*/) { // jc 20150601 renew lift up decay if a led is on,
                                            // ignore highs because I've made them more sensitive above
     uDecay = DECAY;
   } else if (uDecay > 0) {
     uDecay--;
   }

}


// MQTT start


char printbuf[100];




void mqttInit() {
//  Ethernet.begin(mac); // replace by Bridge.begin() if running on a Yun
  Bridge.begin();


  connect();
}


void connect()  // connect to the MQTT broker
{
  char hostname[] = "iot.eclipse.org";
  int port = 1883;
  sprintf(printbuf, "Connecting to %s:%d\n", hostname, port);
  Serial.print(printbuf);


  int rc = ipstack.connect(hostname, port);
  if (rc != 1)
  {
    sprintf(printbuf, "rc from TCP connect is %d\n", rc);
    Serial.print(printbuf);
  }

  Serial.println("MQTT connecting");
  MQTTPacket_connectData data = MQTTPacket_connectData_initializer;  
  data.MQTTVersion = 3;
  data.clientID.cstring = (char*)_id;
  rc = client.connect(data);
  if (rc != 0)
  {
    sprintf(printbuf, "rc from MQTT connect is %d\n", rc);
    Serial.print(printbuf);
  }

  Serial.println("MQTT connected");

}


void sendMessage() {
  if (!client.isConnected())
    connect();

  MQTT::Message message;



  int rc;
  // Send QoS 1 message
  // // Serial.println(payLoadBuf);
  message.qos = MQTT::QOS1;
  message.retained = false;
  message.dup = false;
  message.payload = (void*)payLoadBuf;
//  message.payloadlen = MUESTRAS/2;
  message.payloadlen = MUESTRAS/8;
  rc = client.publish(send_topic, message);
}


// end MQTT functionality ==============================




























// arduino general


void setup()
{


  Serial.begin(9600);
  delay(10000); // give me time to start the yun monitor
servoInit();
  lightOrganSetup();
  // MQTT related tasks
  mqttInit();
}


void loop()
{
static unsigned int counter = 0;


      client.yield(10); // this takes 30 ms. May reduce the parameter // if you get duplicates, increase


  /*
  // lift debug
  while (Serial.available() > 0) {
    uDecay = (Serial.parseInt());
  }
  */




  servoUp = (uDecay > 0);



  servoGo();
  lightOrganGo();
  if (uDecay > 0) {
    counter++;
    if (counter == 30) {


      counter = 0;
      sendMessage();
    }
  }

}





The light organ client code, firmware for the Yún that's built into the remote light organ

 

// needed for MQTT and Process lib
#include <Bridge.h>


// MQTT  includes start
#include <SPI.h>
#include <YunClient.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>


#include <string.h>
// MQTT includes end


// INFINEON includes start
#include <Wire.h>
#include "Infineon.h"




// INFINEON declaration
  Infineon RGBLEDS = Infineon();




// light organ declaration
#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT


#define BAJOS_MEDIOS 4         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 10       // Nº de banda para el corte entre Medios y Agudos




#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los lÃmites




unsigned char bajos,medios,agudos; // Valores calculados para cada canal


byte  pasada,                            // nº de pasada para el cáculo de los lÃmites
      acumBajos,acumMedios,acumAgudos,   // acumuladores de veces que se supera el lÃmite








      limBajos,limMedios,limAgudos;      // lÃmites calculados para cada canal




// start MQTT declarations
char printbuf[100];


YunClient yc; // replace by a YunClient if running on a Yun
IPStack ipstack(yc);
MQTT::Client<IPStack, Countdown> client = MQTT::Client<IPStack, Countdown>(ipstack);
char payLoadBuf[MUESTRAS/2] = {}; // I'm going to give one byte per range, so that I can choose to implement a graph if I want.
const char* subscribe_topic = "PerpetuumEbnerSpectrum";
const char* _id = "1cfee8dd0afc_yun_paradise";
boolean bDataReceived = false;
// end MQTT declarations




// start timer functionality ============================================================
boolean bRunSeconds = false;
boolean bIsRunningSeconds = false;
boolean bRunMinutes = false;
boolean bIsRunningMinutes = false;
boolean bRunHours = false;
boolean bIsRunningHours = false;


void runSeconds() {
  bIsRunningSeconds = true;

  Serial.print("s"); // remove when confident


  if (bDataReceived) {
    bDataReceived = false;
    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda
    bajos = 0;
    for (int i=2; i < BAJOS_MEDIOS; i++){
      bajos += payLoadBuf[i];
    }
    bajos = bajos/2;

    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += payLoadBuf[i];
    }
    medios = medios/2;

    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/8; i++){
      agudos += /*2**/(payLoadBuf[i]);   // jc 20150604 undid because Yun ADC more sensitive or so // 20150601 doubled the highs sesitivity
    }
    agudos = agudos/2;

   // Calculamos si el canal correspondiente
   // supera le lÃmite para encenderlo
   int siBajos  =  bajos  > limBajos;
   int siMedios =  medios > limMedios;
   int siAgudos =  agudos > limAgudos;


   /*
   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);
   */

  sprintf(printbuf, "Writing rgb: %d, %d, %d\n",
  siBajos, siMedios, siAgudos);
  Serial.print(printbuf);
    RGBLEDS.I2CWRITE6BYTES(ADDRESS, INTENSITY_RGB, siBajos*0xFFF, siMedios*0xFFF, siAgudos*0xFFF);


   // Utilizamos las veces que se supera para
   // recalcular el lÃmite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;

   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
   }

  }

  bRunSeconds = false;
  bIsRunningSeconds = false;
}


void runMinutes() {
  bIsRunningMinutes = true;



  bRunMinutes = false;
  bIsRunningMinutes = false;
}


void runHours() {
  bIsRunningHours = true;


  bRunHours = false;
  bIsRunningHours = false;
}


void timerInit() {
  // initialize timer1 for 1 second ticks; ISR(TIMER1_COMPA_vect) will be called as interrupt handler
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;


  OCR1A = 62500;            // compare match register 16MHz/256/1Hz
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS12);    // 256 prescaler
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}


ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  static unsigned int uSeconds = 0;
  uSeconds++; // every call is a second
  bRunSeconds = true; // so yes, flag that the seconds handler should be called
  bRunMinutes = ! (uSeconds % 60); // each 60th second, flag that the minutes handler should be called
  if (uSeconds > 3599) { // every hour
    bRunHours = true; // flag that the hours handler should be called
    uSeconds = 0; // and start over
  }
}


void timerTasks() {
  if (bRunSeconds && ! bIsRunningSeconds) { // timer interrupt flagged that seconds handler should be called
    runSeconds(); // but we only run it if it's not active
  }
  if (bRunMinutes && ! bIsRunningMinutes) { // timer interrupt flagged that minutes handler should be called
    runMinutes(); // but we only run it if it's not active
  }
  if (bRunHours && ! bIsRunningHours) { // timer interrupt flagged that hours handler should be called
    runHours(); // but we only run it if it's not active
  }
}


// end timer functionality =====================================




// start MQTT functionality ====================================




void mqttInit() {
//  Ethernet.begin(mac); // replace by Bridge.begin() if running on a Yun
  Bridge.begin();


  Serial.println("Enchanted Objects Remote Light Organ");
  connect();
}


void messageArrived(MQTT::MessageData& md) // this handler is called when a subscribed MQTT message arrives
{
  MQTT::Message &message = md.message;
  bDataReceived = true;

  // big chance that I'm off here by translating bytes, words, chars. But I Don't Have A Debugger in the Arduino IDE to Check.
  for (int ii = 0; ii < message.payloadlen; ii++ ) {
    payLoadBuf[ii] = ((char*)message.payload)[ii];
  }
  // debug code
  sprintf(printbuf, "Message arrived: len %d, retained %d, dup %d, packetid %d\n",
  message.payloadlen, message.retained, message.dup, message.id);
  Serial.print(printbuf);
/*
  sprintf(printbuf, "Payload %s\n", (char*)message.payload);


  Serial.print(printbuf);

  // sprintf(printbuf, "Topic len %d\n", md.topicName.lenstring);
  int i;
  for (i = 0; i < (md.topicName.lenstring.len); i++) {
    printbuf[i] = md.topicName.lenstring.data[i];
  }
  printbuf[(md.topicName.lenstring.len)]=  '\n';
  printbuf[md.topicName.lenstring.len + 1]=  0;

  */
}


void connect()  // connect to the MQTT broker
{
  char hostname[] = "iot.eclipse.org";
  int port = 1883;
  sprintf(printbuf, "Connecting to %s:%d\n", hostname, port);
  Serial.print(printbuf);
  int rc = ipstack.connect(hostname, port);
  if (rc != 1)
  {
    sprintf(printbuf, "rc from TCP connect is %d\n", rc);
    Serial.print(printbuf);
  }

  // // Serial.println("MQTT connecting");
  MQTTPacket_connectData data = MQTTPacket_connectData_initializer;  
  data.MQTTVersion = 3;
  data.clientID.cstring = (char*)_id;
  rc = client.connect(data);
  if (rc != 0)
  {
    sprintf(printbuf, "rc from MQTT connect is %d\n", rc);
    Serial.print(printbuf);
  }
  Serial.println("MQTT connected");
  rc = client.subscribe(subscribe_topic, MQTT::QOS1, messageArrived);
  if (rc != 0)
  {
    sprintf(printbuf, "rc from MQTT subscribe is %d\n", rc);
    Serial.print(printbuf);
  }
  Serial.println("MQTT subscribed");

}




// end MQTT functionality ==============================


// start INFINEON functionality =====


void rgbSetup() {
    Wire.begin();
    Serial.println("polling led shield...");
    while (RGBLEDS.on != 1) // Wait for shield to respond, keep setting the values till it does
    {

      Serial.println("led shield setup");


      RGBLEDS.I2CWRITE2BYTES (ADDRESS, FADERATE, 0x0000); // Immediate fade
      Serial.println("faderate set up");
      RGBLEDS.I2CWRITE2BYTES (ADDRESS, DIMMINGLEVEL, 0x0000); // 0% brightness level
      RGBLEDS.on = RGBLEDS.I2CREAD(ADDRESS, READ_DIMMINGLEVEL); // Request for brightness level
      if (RGBLEDS.message == 1 && RGBLEDS.on == 0) // If message received and dimming level = 0%, "message" is set in the I2CREAD function
      {
        Serial.println("message check for 0");
        RGBLEDS.message = 0;
        RGBLEDS.on = 1; // break out of loop
      }
    }
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, OFFTIME_RED, 0x38); // Set off-time of red channel to 0x38
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, OFFTIME_GREEN, 0x38); // Set off-time of green channel to 0x39
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, OFFTIME_BLUE, 0x38); // Set off-time of blue channel to 0x38
    RGBLEDS.I2CWRITE6BYTES (ADDRESS, CURRENT_RGB, 0x05, 0x05, 0x05); // max:  0x80 = 780mA, I need 15 for standard leds, (0x03), but dimmed down to 0x02 because that's enough as max
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, FADERATE, 0x0000); // Fade Rate between intensities --> 0.0s
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, WALKTIME, 0x0000); // walk time between colors = 0s
    RGBLEDS.I2CWRITE6BYTES (ADDRESS, INTENSITY_RGB, 0x0555, 0x0555, 0x0555); // low level White Light
    RGBLEDS.I2CWRITE2BYTES (ADDRESS, DIMMINGLEVEL, 0x0FFF); // Maximum dimming level means inensity settings are directly used
    Serial.println("led shield active \n");
}






// end infineon functionality ======






void setup()
{


  Serial.begin(9600);
  delay(10000); // give me time to start the yun monitor


  // MQTT related tasks
  mqttInit();

  // INFINEON related tasks
  rgbSetup();


  // timer related tasks
  timerInit();
}


void loop()
{

  timerTasks();











































   // task: keep MQTT alive
  client.yield(10); // this takes 30 ms. May reduce the parameter // if you get duplicates, increase



}





 

 

.

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

June 13 calendar scan

 

This is my first installment in the BONUS series where I'm tapping into the turntable's audio spectrum data that I'm publishing on the internet.

light organ loads of leds

 

My first bonus design is a remote light organ. I'm building it with an Arduino Yún, the Infineon RGB Led Shield and a bunch of LEDs.

Part 1 is my log on the build, and trying out if the Arduino-to-Infineon-toLEDs works.

 

Re-use and Re-purpose

 

I have kind of a head start here. For a previous element14 event, I have been using the RGB shield before.

That enabled me to just pull up the sketch that I used back then.

(I'll be able to do the same when building an MQTT listener on the Yun - been there done that.)

 

 

It's been a day of old school electronics. I pulled out the trusty soldering iron from my school days and did some trough-hole work.

Photo 12-06-15 09 48 48.jpg

The remote organ will fit in a little shoe box, so I cut out a plate of carton to mount everything on.

Photo 12-06-15 11 08 49.jpg

 

I've connected the things up and tidied the layout a bit.

Photo 12-06-15 12 09 16.jpg Photo 12-06-15 12 09 26.jpg

 

Sketch loaded - and it works. At the moment it's animating each of the 3 rows in 4 intensities.

That 'll change in part 2, where I'll tap into the MQTT data and turn this into a light organ.

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

20150609.jpeg

 

 

Today is a turning point for the project. The turntable is finished.

Speed control and light organ with servo lift are built in.

And now that the table is publishing data to the cloud, it's officially a Thing.

 

clean desk

 

If I was a stressful person, a big weight would fall of my shoulders now.

I'm not a stressful person. But I am happy that I reached this point.

A point of luxury and freedom. I still have loads of time to tap into the cloud part. To creativity!

 

 

 

What's Built into the Table

 

There are two Arduinos -Yún and UNO, the Infineon Motor Shield and various electronics.

The Yún has audio sample, light organ+servo lift, and MQTT publishing duty.

 

 

arduino yun under proto board

 

The protoboard on top of the Yún has a one transistor pre-amplifier + DC biaser. The duty of that little circuit is to push the AC audio into the 0->5V range of the Arduino ADC.

It also holds the connectors for the stepper motor and the light organ LEDs.

 

The UNO serves as motor speed controller. It measures the speed from the IR optocoupler that I've built for this project.

 

Photo 15-04-15 17 57 49.jpg

 

The UNO uses the Infineon Motor Shield to control the motor, and also shows what's happening on 3 LEDs (fast - on speed -slow).

 

infineon motor shield

 

Everything runs now, and the Perpetuum Ebner is happily sharing the spectrum analysis on the internet.

 

What Now?

 

I can spend the remaining time on the satellite ideas. They'll tap into that MQTT stream and do something with it.

And unbox the SAMA5D4 again. It's been neglected the last weeks.

And I can also spend a sunny warm evening outside to finalize the mounting of the devices on the sub-chassis.

As you can see on the photo below, there's room for (invisible) improvement there.

 

Photo 09-06-15 20 17 22.jpg

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

20150605.jpeg

 

Today I'm taking that great leap to the cloud. The Perpetuum Ebner goes online and publishes what's happening.

 

 

What is the Turntable Publishing?

 

I have 3 interesting sets of data available in my design.

  • The raw sampled data, 128 8-bit samples covering the audio frequency range (signal B).
  • That same information transposed to the frequency domain: 64 buckets, each representing a subset of that frequency spectrum (signal C).
  • Those 64 buckets grouped into a Bass, Mid and High range (signal D). That's the info that drives the LEDs of the light organ.

 

enchanted_objects enchanted_player

 

I could publish any of the three. To make the decision, I thought about what the consumer is going to do with the data.

 

My plan is to make a remote light organ, and a WiFi spectrum display.

For the light organ, signal D would be OK. In that case I could even simplify the signal and stick with 2 bits to hold the on-off information for the 3 LEDs.

But if I want to keep the options open for a wireless spectrum display, I'll need signal C. That signal is also usable as source for the remote light organ.

 

The easiest decision would be to send signal B, the full samples. But engineering is about making decisions and trade-offs.

I'm not painting myself in a corner here. In the future I can always switch to signal B if needed. I could publish them in parallel until all subscribers are converted, and then switch of signal C.

 

Firmware with MQTT Publishing

 

The firmware builds upon the previous versions. The only addition is that I now publish the 64 buckets every 20th sample.

This is again a trade-off. My design can go faster, but I figure that eclipse.org doesn't like to be hammered by my data every few milliseconds.

So I settled for the lowest speed that would still give an interesting and lively visual result.

 

 

I subscribed to my data with eclipse PaHo client. The screen capture above shows the received data. It's normal that the characters don't display right, because the payload isn't text. It's 64 8-bit nuggets of info.

 

And here's the latest firmware:

 

 

// includes for servo
#include <Servo.h>


// includes for light organ
#include <fix_fft.h>




// MQTT  includes start
#include <Bridge.h>
#include <SPI.h>
#include <YunClient.h>
#include <IPStack.h>
#include <Countdown.h>
#include <MQTTClient.h>


#include <string.h>
// MQTT includes end








// servo declaration


Servo servo;


#define SERVO_PIN 9
#define SERVO_DELAY 25
#define SERVO_BASE 90
#define SERVO_TOP (SERVO_BASE + 61)


int servoPos = SERVO_BASE;
bool servoUp = false;






// light organ declaration


#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT
#define LOGM 7                 // Logaritmo en base 2 del número de muestras


#define BAJOS_MEDIOS 7         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35       // Nº de banda para el corte entre Medios y Agudos


#define BPIN  13                // Pin de salida Bajos
#define MPIN  12               // Pin de salida Medios
#define APIN  11               // Pin de salida Agudos
#define TIMERPIN 8




#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los lÃmites


char data[MUESTRAS];           // Array con los valores muestreados (parte real)
char im[MUESTRAS];             // Array con los valores muestreados (parte imaginaria)


unsigned char salida[MUESTRAS/2];  // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos; // Valores calculados para cada canal


byte  pasada,                            // nº de pasada para el cáculo de los lÃmites
      acumBajos,acumMedios,acumAgudos,   // acumuladores de veces que se supera el lÃmite
      limBajos,limMedios,limAgudos;      // lÃmites calculados para cada canal




// start MQTT declarations
YunClient c; // replace by a YunClient if running on a Yun
IPStack ipstack(c);
MQTT::Client<IPStack, Countdown> client = MQTT::Client<IPStack, Countdown>(ipstack);
char payLoadBuf[MUESTRAS/2] = {}; // I'm going to give one byte per range, so that I can choose to implement a graph if I want.
const char* send_topic = "PerpetuumEbnerSpectrum";
const char* _id = "1cfee8dd0afc_yun";
// end MQTT declarations






// both:
// decay is a decreasing counter that tells how long the servo will stay up after the last led flashed.
// reset to DECAY each time a led lights up
// looses one live each time no leds are active
#define DECAY 150U
unsigned int uDecay = 0U;




// servo functionality


void servoInit() {
    servoUp = false;
    servo.attach(SERVO_PIN);
    servoPos = SERVO_BASE;
    servo.write(SERVO_BASE);
    delay(1000);
}


void servoGoUp() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos < SERVO_TOP) {
    servoPos++;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGoDown() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos > SERVO_BASE) {
    servoPos--;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGo() {
  if (servoUp) {
    servoGoUp();
  } else {
    servoGoDown();
  }
}




// light organ fuctions


/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
    double muestrasMenosUno = (double(MUESTRAS) - 1.0);
  // Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
    for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
        double indiceMenosUno = double(i);
        double ratio = (indiceMenosUno / muestrasMenosUno);
        double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
  vData[i] *= factorPeso;
  vData[MUESTRAS - (i + 1)] *= factorPeso;
    }
}




void lightOrganSetup() {
    // Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
    // como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
    // Es decir podemos medir en teoria hasta unos 19KHz,
    // que para este proyecto sobra.
    bitWrite(ADCSRA,ADPS2,1);
    bitWrite(ADCSRA,ADPS1,0);
    bitWrite(ADCSRA,ADPS0,1);


    // Como la señal es muy baja,utilizamos la referencia interna
    // de 1.1 V en vez de la de defecto de 5 V.
    analogReference(INTERNAL);  
   
    // Salidas para los canales de Bajos,Medios y Agudos
    pinMode(BPIN,OUTPUT);
    pinMode(MPIN,OUTPUT);
    pinMode(APIN,OUTPUT);
   
   
    // debug
    pinMode(TIMERPIN,OUTPUT);
    Serial.begin(9600);


    // Variables para el cálculo de los lÃmites
    pasada = 0;
    acumBajos = acumMedios = acumAgudos = 0;
    limBajos = limMedios = limAgudos = 50; 

  }


void lightOrganGo() {
    // Realizamos el muestreo
//    Serial.println("Start sampling:");
    digitalWrite(TIMERPIN, HIGH);
    for( int i=0; i < MUESTRAS; i++) {
       data[i] = analogRead(0)/4 -128;  //Convertimos de 0..1024 a -128..127                                
       im[i] = 0;                       // parte imaginaria = 0                         
    }
    digitalWrite(TIMERPIN, LOW);
   
/*     
    {
    Serial.println(" samples\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
   
    }
*/   


    // Aplicamos la ventana de Hann
    aplicaVentana (data);


/*
    Serial.println(" window\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/
    // Calculamos la FFT
    fix_fft(data,im,LOGM,0);
   
    // Sólo nos interesan los valores absolutos, no las fases, asi que
    // calculamos el módulos de los vectores re*re + im*im.
    // Dado que los valores son pequeños utilizamos el cuadrado
    for (int i=0; i < MUESTRAS/2; i++){
       salida[i] = data[i] * data[i] + im[i] * im[i];
       payLoadBuf[i] = salida[i];
    }
/*
    Serial.println(" result\n");
     for( int i=0; i < MUESTRAS/2; i++) {
      Serial.print(salida[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/
  
    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda
    bajos = 0;
    for (int i=2; i < BAJOS_MEDIOS; i++){
      bajos += salida[i];
    }
    bajos = bajos/2;
   
    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += salida[i];
    }
    medios = medios/2;
   
    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
      agudos += /*2**/(salida[i]);   // jc 20150604 undid because Yun ADC more sensitive or so // 20150601 doubled the highs sesitivity
    }
    agudos = agudos/2;
   
   // Calculamos si el canal correspondiente
   // supera le lÃmite para encenderlo
   int siBajos  =  bajos  > limBajos;
   int siMedios =  medios > limMedios;
   int siAgudos =  agudos > limAgudos;
  
   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);
  
   // Utilizamos las veces que se supera para
   // recalcular el lÃmite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;
  
   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
   }


   if (siBajos | siMedios /*| siAgudos*/) { // jc 20150601 renew lift up decay if a led is on,
                                            // ignore highs because I've made them more sensitive above
     uDecay = DECAY;
   } else if (uDecay > 0) {
     uDecay--;
   }
  
}


// MQTT start


char printbuf[100];




void mqttInit() {
//  Ethernet.begin(mac); // replace by Bridge.begin() if running on a Yun
  Bridge.begin();


  connect();
} 


void connect()  // connect to the MQTT broker
{
  char hostname[] = "iot.eclipse.org";
  int port = 1883;
  sprintf(printbuf, "Connecting to %s:%d\n", hostname, port);
  Serial.print(printbuf);


  int rc = ipstack.connect(hostname, port);
  if (rc != 1)
  {
    sprintf(printbuf, "rc from TCP connect is %d\n", rc);
    Serial.print(printbuf);
  }

  Serial.println("MQTT connecting");
  MQTTPacket_connectData data = MQTTPacket_connectData_initializer;      
  data.MQTTVersion = 3;
  data.clientID.cstring = (char*)_id;
  rc = client.connect(data);
  if (rc != 0)
  {
    sprintf(printbuf, "rc from MQTT connect is %d\n", rc);
    Serial.print(printbuf);
  }

  Serial.println("MQTT connected");

}


void sendMessage() {
  if (!client.isConnected())
    connect();

  MQTT::Message message;



  int rc;
  // Send QoS 1 message
  // // Serial.println(payLoadBuf);
  message.qos = MQTT::QOS1;
  message.retained = false;
  message.dup = false;
  message.payload = (void*)payLoadBuf;
  message.payloadlen = strlen(payLoadBuf)+1;
  rc = client.publish(send_topic, message);
} 


// end MQTT functionality ==============================




























// arduino general


void setup()
{


  Serial.begin(9600);
  delay(10000); // give me time to start the yun monitor
servoInit();
  lightOrganSetup();
  // MQTT related tasks
  mqttInit();
}


void loop()
{
static unsigned int counter = 0;




  /*
  // lift debug
  while (Serial.available() > 0) {
    uDecay = (Serial.parseInt());
  }
  */




  servoUp = (uDecay > 0);



  servoGo(); 
  lightOrganGo();
  if (uDecay > 0) {
    counter++;
    if (counter == 20) {
      counter = 0;
      sendMessage();
    }
  }

}

 

Feel free to subscribe and animate your own gizmo.

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

enchanted objects

 

It's time to connect my turntable to the internet. We're in the last month, and I haven't sent a single bit to the cloud.

That's about to change.

I'm preparing my fixture to publish information. It's going to be a two-step exercise.

First, I'm going to share the light organ info. I'll send the state of my Highs; Mids and Lows LEDs to the world.

Once I have that working, I'm going to add speed info: is my Perpetuum Ebner running slow, fast or perfect.

 

Arduino Yun

 

 

 

From UNO to Yún

 

My UNO was running the light organ and servo lift code. Today's exercise is taking care that that info is ported to the Yún.

And to check that there's enough space left to talk to the cloud.

That was a fairly easy task. To cut a long story short: the code just ported straight away.

 

 

There's a tendency to play down the Arduino concept. But I'm very sold into it.

It's a great platform to try out new things with an easy step-in cost; there are libraries for so many things.

If you want to go low level, you can just do that. Nothing stops you from going bare metal.

When I have difficulties talking to a particular ship, I often turn to an Arduino to set up a prototype that I can probe with my logic analyzer.

 

And in this particular project, I can just switch to a WiFI enabled Yún. Just like that.

It's at those moments that you really understand why Arduino has had such an impact.

 

There wasn't any software issue during the switch. I changed port and board type, loaded the code, and off I went.

There is a slight difference in sensitivity of the ADC that I have to fine-tune.

I haven't found the reason for it yet, but particularly in the high frequency range, the Yún's ADC is more sensitive than the UNO's.

This results in my green Highs LED being on more often.

That's something I can compensate in software. But I'm still curious on why that happens. I'll have to pull up Atmel's data sheets.

 

The second thing to consider when porting, is the form factor of the Yún. More specific: the USB and ethernet connectors.

The protoboard that's part of the enchanted objects package doesn't fit out of the box.

This is what I have done to it:

 

Photo 28-05-15 10 40 47.jpg

I used my fake dremel and sliced the board in half. That way it fits on the Yún.

I loose the SMD pads - something I would have liked to use, I have some nice Texas Instruments SMD opamps - but you can't have it all.

 

Photo 03-06-15 19 25 59.jpg

 

 

With both hardware and software ported, I'm ready to reach for the clouds.

 

MQTT and Me

 

I have previous experience with the protocol. I've used it for my Christmas Wreath of Things.

If I'm expecting an issue, it would be space or compatibility related.

Regarding space, I'm optimistic. I tried to add the MQTT libraries to my sketch, and initialize some objects to get an appreciation of the memory use.

 

Sketch uses 16,356 bytes (57%) of program storage space. Maximum is 28,672 bytes.
Global variables use 1,138 bytes (44%) of dynamic memory, leaving 1,422 bytes for local variables. Maximum is 2,560 bytes.

 

Nothing alarming there.

 

Library incompatibility is something more difficult in the Arduino world. In particular when libraries are using lower level components of the controller.

I have issues in my design. The motor PWM engine, speed sampler and servo libraries compete for the same hardware timer.(and are incompatible because of that).

I solved that by using two different Arduinos. It's a bit silly because one ATMega has enough juice to do all the work.

The right approach would be to either find libraries that work together, or write my own firmware.

I'll leave that as an academic exercise for the reader.

I'm just hoping here that the Servo lib is compatible with the MQTT and Bridge part...

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

20150601.jpg

 

In this episode, I'm combining the light organ and the servo control into one Arduino UNO..

It's also the first provisional attempt of mounting into the base of the turntable.

 

 

igonic.jpg

 

How the Light Organ and the Servo Lift Work Together

 

I want the light organ and the lift to just work together, without me having to worry about them.

In previous posts I have introduced the autonomous light organ and the autonomous lift.

They are now combined on a single Arduino, but still have a life of their own.

 

As soon as the music plays, the light organ will do its work and starts flashing its LEDs.

At the same time, it will tell the servo to move up and stay up for a while (its lifespan: 150 while loops).

Each time a LED is active, it renews the lifespan. But when there's no LED on, one point is taken from the lifespan.

If all the lifepoints are taken, the servo retreats and moves the LEDS back inside the case.

 

A video says more than 1000 words:

 

 

The code is simple. Search for the word decay to find out what's happening.

 

 

 

 

 

#include <Servo.h>
#include <fix_fft.h>


// servo declaration


Servo servo;


#define SERVO_PIN 9
#define SERVO_DELAY 25
#define SERVO_BASE 90
#define SERVO_TOP (SERVO_BASE + 61)


int servoPos = SERVO_BASE;
bool servoUp = false;






// light organ declaration


#define MUESTRAS 128           // Numero de muestras para el cálculo de la FFT
#define LOGM 7                 // Logaritmo en base 2 del número de muestras


#define BAJOS_MEDIOS 7         // Nº de banda para el corte entre Bajos y Medios
#define MEDIOS_AGUDOS 35       // Nº de banda para el corte entre Medios y Agudos


#define BPIN  13                // Pin de salida Bajos
#define MPIN  12               // Pin de salida Medios
#define APIN  11               // Pin de salida Agudos
#define TIMERPIN 8




#define MAX_PASADAS 10         // Nº de pasadas para el cálculo de los lÃmites


char data[MUESTRAS];           // Array con los valores muestreados (parte real)
char im[MUESTRAS];             // Array con los valores muestreados (parte imaginaria)


unsigned char salida[MUESTRAS/2];  // Valores obtenidos de la FFT (espectro de 64 bandas)
unsigned char bajos,medios,agudos; // Valores calculados para cada canal


byte  pasada,                            // nº de pasada para el cáculo de los lÃmites
      acumBajos,acumMedios,acumAgudos,   // acumuladores de veces que se supera el lÃmite
      limBajos,limMedios,limAgudos;      // lÃmites calculados para cada canal








// both:
// decay is a decreasing counter that tells how long the servo will stay up after the last led flashed.
// reset to DECAY each time a led lights up
// looses one live each time no leds are active
#define DECAY 150U
unsigned int uDecay = 0U;




// servo functionality


void servoInit() {
    servoUp = false;
    servo.attach(SERVO_PIN);
    servoPos = SERVO_BASE;
    servo.write(SERVO_BASE);
    delay(1000);
}


void servoGoUp() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos < SERVO_TOP) {
    servoPos++;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGoDown() {

  if (servoPos < SERVO_BASE) {
    servoInit();
  }

  if (servoPos > SERVO_BASE) {
    servoPos--;
    servo.write(servoPos);
    delay(SERVO_DELAY);
  }
}


void servoGo() {
  if (servoUp) {
    servoGoUp();
  } else {
    servoGoDown();
  }
}




// light organ fuctions


/*
* Funcion que aplica una ventana de Hann a los datos muestreados para reducir el
* efecto de las discontinuidades en los extremos
*/
void aplicaVentana (char *vData) {
    double muestrasMenosUno = (double(MUESTRAS) - 1.0);
  // Como la ventana es simétrica , se calcula para la mitad y se aplica el factor por los dos extremos
    for (uint8_t i = 0; i < MUESTRAS/2 ; i++) {
        double indiceMenosUno = double(i);
        double ratio = (indiceMenosUno / muestrasMenosUno);
        double factorPeso = 0.5 * (1.0 - cos(6.28 * ratio));
  vData[i] *= factorPeso;
  vData[MUESTRAS - (i + 1)] *= factorPeso;
    }
}




void lightOrganSetup() {
    // Configuramos el prescaler a 32 -> 16Mhz/32 = 500 KHz
    // como cada conversion son 13 ciclos 500/13 ~ 38.4KHz
    // Es decir podemos medir en teoria hasta unos 19KHz,
    // que para este proyecto sobra.
    bitWrite(ADCSRA,ADPS2,1);
    bitWrite(ADCSRA,ADPS1,0);
    bitWrite(ADCSRA,ADPS0,1);


    // Como la señal es muy baja,utilizamos la referencia interna
    // de 1.1 V en vez de la de defecto de 5 V.
    analogReference(INTERNAL); 
  
    // Salidas para los canales de Bajos,Medios y Agudos
    pinMode(BPIN,OUTPUT);
    pinMode(MPIN,OUTPUT);
    pinMode(APIN,OUTPUT);
  
  
    // debug
    pinMode(TIMERPIN,OUTPUT);
    Serial.begin(9600);


    // Variables para el cálculo de los lÃmites
    pasada = 0;
    acumBajos = acumMedios = acumAgudos = 0;
    limBajos = limMedios = limAgudos = 50;

  }


void lightOrganGo() {
    // Realizamos el muestreo
//    Serial.println("Start sampling:");
    digitalWrite(TIMERPIN, HIGH);
    for( int i=0; i < MUESTRAS; i++) {
       data[i] = analogRead(0)/4 -128;  //Convertimos de 0..1024 a -128..127                               
       im[i] = 0;                       // parte imaginaria = 0                        
    }
    digitalWrite(TIMERPIN, LOW);
  
/*    
    {
    Serial.println(" samples\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
  
    }
*/  


    // Aplicamos la ventana de Hann
    aplicaVentana (data);


/*
    Serial.println(" window\n");
    for( int i=0; i < MUESTRAS; i++) {
      Serial.print(data[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/
    // Calculamos la FFT
    fix_fft(data,im,LOGM,0);
  
    // Sólo nos interesan los valores absolutos, no las fases, asi que
    // calculamos el módulos de los vectores re*re + im*im.
    // Dado que los valores son pequeños utilizamos el cuadrado
    for (int i=0; i < MUESTRAS/2; i++){
       salida[i] = data[i] * data[i] + im[i] * im[i];
    }
/*
    Serial.println(" result\n");
     for( int i=0; i < MUESTRAS/2; i++) {
      Serial.print(salida[i], DEC);
      Serial.print(",");
    }
    Serial.println(" ---");
*/

    // Ahora repartimos las bandas entre las 3 salidas
    // En vez de sacar la media, utilizo sólo el valor máximo de
    // una banda
    bajos = 0;
    for (int i=2; i < BAJOS_MEDIOS; i++){
      bajos += salida[i];
    }
    bajos = bajos/2;
  
    medios = 0;
    for (int i=BAJOS_MEDIOS ; i < MEDIOS_AGUDOS; i++){
      medios += salida[i];
    }
    medios = medios/2;
  
    agudos = 0;
    for (int i=MEDIOS_AGUDOS; i < MUESTRAS/2; i++){
      agudos += 2*(salida[i]);   // jc 20150601 doubled the highs sesitivity
    }
    agudos = agudos/2;
  
   // Calculamos si el canal correspondiente
   // supera le lÃmite para encenderlo
   int siBajos  =  bajos  > limBajos;
   int siMedios =  medios > limMedios;
   int siAgudos =  agudos > limAgudos;

   digitalWrite(BPIN,siBajos ? HIGH : LOW);
   digitalWrite(MPIN,siMedios? HIGH : LOW);
   digitalWrite(APIN,siAgudos? HIGH : LOW);

   // Utilizamos las veces que se supera para
   // recalcular el lÃmite y evitar que con los
   // cambios de volumen los canales se saturen
   // o no funcionen.
   acumBajos  += siBajos;
   acumMedios += siMedios;
   acumAgudos += siAgudos;

   if ( ++pasada > MAX_PASADAS ) {
      pasada = 0;
      limBajos  = 20 + acumBajos*5;
      limMedios = 20 + acumMedios*5;
      limAgudos = 20 + acumAgudos*5;
      acumBajos  = 0;
      acumMedios = 0;
      acumAgudos = 0;
   }


   if (siBajos | siMedios /*| siAgudos*/) { // jc 20150601 renew lift up decay if a led is on,
                                            // ignore highs because I've made them more sensitive above
     uDecay = DECAY;
   } else if (uDecay > 0) {
     uDecay--;
   }

}


// arduino general


void setup()
{


  Serial.begin(9600);
  servoInit();
  lightOrganSetup();
//  Serial.println("Menu: Up: 1, Down: 0");
}


void loop()
{
  // lift debug
  while (Serial.available() > 0) {
    uDecay = (Serial.parseInt());
  }




  servoUp = (uDecay > 0);



  servoGo();
  lightOrganGo();

}


 

Next, I,ll move the code to a Yún and include MQTTing of the Highs, Mids and Lows info to the cloud.

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

Image (3).jpg

 

No electronics in today's post. Just a few pictures of the work on the sub-chassis.

I'm avoiding to drill holes in the nice turntable coffer. I'm crafting a light carton base that nicely fits inside the turntable.

The material I use is from my son's school project. He's building an architectural model of a house.

 

Photo 27-05-15 21 51 21.jpg

 

Building the sub-chassis

 

It's just the fancy name for a piece of carton. As prototype I used part of a pizza box.

 

Photo 27-05-15 10 59 14.jpg

 

I made that fit using the trial and error method.

Photo 27-05-15 10 59 36.jpg

 

Then I used it as template for the cardboard.

 

Photo 27-05-15 11 00 15.jpg

 

Photo 27-05-15 11 15 14.jpg

 

This is the end result: a nicely fitting mounting plate.

After cutting it out, I re-traced the sides with an extra line of red sharpie to give it that professional finished look .

 

Photo 27-05-15 11 26 04.jpg

 

Having this removable base plate will make it easier to work on the different parts.

And I can take everything out at once when I have to revert the turntable to its original state.

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

Image (6).jpg

This is my first SAMA5D4 firmware that takes a reasonable sample of audio range signals.

I'm using the ADC buffer mechanism. The converter fills the buffer in the background.

I'm reading the values and log them. Later I'll use them to drive the light organ of the enchanted table.

 

Photo 25-05-15 21 18 40.jpg

 

 

What's Happening in the Firmware?

 

I'm doing exactly the same as what's documented on IioAdcDriver < Linux4SAM < TWiki.

But in C++. You can find the instructions back in the code.

For clarity, I'm using the same comments as on the AT91 pages.

 

Set up the channels in use

linux

echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en



c++

      ofstream osChannel("/sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en", ios::out);
      if (osChannel.is_open()) {
        osChannel << "1";
        osChannel.close();
      } else {
        cout << "Unable to open file " << "/sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en" << "\n";
      }



Set up the trigger we want to us

linux

echo "fc034000.adc-dev0-continuous" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger



c++

      ofstream osTrigger("/sys/bus/iio/devices/iio:device0/trigger/current_trigger", ios::out);
      if (osTrigger.is_open()) {
        osTrigger << "fc034000.adc-dev0-continuous";
        osTrigger.close();
      } else {
        cout << "Unable to open file " << "/sys/bus/iio/devices/iio:device0/trigger/current_trigger" << "\n";
      }


 

Set up the buffer length

linux

echo 100 > /sys/bus/iio/devices/iio:device0/buffer/length



c++

      ofstream osLength("/sys/bus/iio/devices/iio:device0/buffer/length", ios::out);
      if (osLength.is_open()) {
        osLength << MUESTRAS;
        osLength.close();
      } else {
        cout << "Unable to open file " << "/sys/bus/iio/devices/iio:device0/buffer/length" << "\n";
      }



Enable the capture

linux

echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable



c++
      ofstream osEnable("/sys/bus/iio/devices/iio:device0/buffer/enable", ios::out);
      if (osEnable.is_open()) {
        osEnable << "1";
        osEnable.close();
      } else {
        cout << "Unable to open file " << "/sys/bus/iio/devices/iio:device0/buffer/enable" << "\n";
      }



 

 

After that point, the ADC starts filling the buffer - available as character device  /dev/iio:device0.

I then go on and read these 128 samples of the buffer. After that, my code will go to do the light organ work - a topic for the next post.

 

There's still some dodgy code in my program. My algorithm to read from that character device isn't good.

I do know how to do it properly (I have the c example code from generic_buffer.c available).

I was just hoping that I could simplify that code using a c++ character stream. I just don't seem to be able to use the stream mechanism reliable with a volatile character device yet.

I sometimes receive the two bytes that form my value in reverse order. I know that's me, because the sample code does it just fine. So there's still work to do.

 

 

  while(1) {
    ifstream myfile(fileName, ios_base::binary);
    if (myfile.is_open()) {




      for( i=0; i < MUESTRAS; i++) {




        myfile >> uSampleA;
        myfile >> uSampleB;


        /**
         * Obviously, I don't know what I'm doing here.
         * I need to study generic_buffer.c to really understand
         * how to interpret /dev/iio:device0
         * I have a 10 bits result, most likely stored in 2 bytes
         * But the way I'm reading it back seems to give a random
         * order of the high and low bytes
         *
         * I'd better start learning the way to properly read the buffer
         * but for my light organ, the errors are in the noise range
         */


        uShiftedSample = uSampleA << 8;
        uShiftedSample |= uSampleB;


        if (uShiftedSample > 1023U) {
          uShiftedSample = uSampleB << 8;
          uShiftedSample |= uSampleA;
        }


        //cout << uSampleA << " " << uSampleB << " " << uShiftedSample << "\n";


//        data[i] = uShiftedSample/4-128; //Convertimos de 0..1024 a -128..127
        data[i] = uShiftedSample; //Convertimos de 0..1024 a -128..127
        im[i] = 0;                    // parte imaginaria = 0


      }


      cout << "\nsamples :\n";
      for( i=0; i < MUESTRAS; i++) {
        cout << (int)data[i] << ",";
      }
      cout << "\n";




    } else {
      cout << "Unable to open file " << fileName << "\n";
    }


    // taken out of sample loop
    myfile.close();
  }



 

For my light organ however, this sample quality will do.

In the next installment I'll transpose the samples to the -128..127 range.

Then to go through the FFT library, and I'll chop them up in bass, middle and treble...

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

I'm now at the point in the project where I can match what I have with what I can.

 

Photo 22-05-15 23 36 57.jpg

 

I overestimated my skills when I announced that I would try to integrate a Shazam-like service into my player.

Or more honest: I didn't overestimate. I set this a s a goal to force myself to reach as far as I could.

At that time, I expected that I'd be faster to adapt embedded  Linux.

Based on no particular evidence, I predicted for @self that I'd be on track with that technology fast.

 

And I didn't get on track fast. It was a steep curve. And that steep curve resulted into unexpected results.

My flashy goals are out of reach. No way will I be able to get that song recognition part integrated.

It's a magnitude more complex than what I learned up till today. I'm  a realist. That's not something I'm going to achieve right now.

 

But I think that this is offset by something more valuable for the outside world. I've shared my struggle.

I will not deliver that mystical enchanted song recognition system.

But on the way of not delivering that, I have written a trail of experiences and HOW-TO's that may help the community.

I learned a lot. And I hope that this opens up opportunities for you.

Along the way, this may have caused some discomfort left or right. It will turn out alright in the end.

 

Achievable Goals

 

First, there are all the things that I've shown before (turntable motor drive, speed detection, control loop, audio sampling, FFT and spectrum analysis, servo lift).

As a stretch for myself, I'm going to port as much as I can of that into the SAMADA5D4.  As a learning opportunity for @self,

And as a tribute to the power of that processor.

 

To make it connected, I plan to beam a part of the audio information to the cloud.

I'll broadcast a message with the bass, mid and treble info of the record currently playing.

Subscribers all over the world (most likely me only ) can subscribe and animate something based on that.

My animated thing will be an internet connected light organ, powered by Yún and Infineon LED shield. Yours may be a frequency analyzer visualization thingy..

Latency  be damned .

 

Ah, and I don't have high power leds for the light organ. Send me some please.

 

 

Table of Contents
Chapter 1: Fix the turntable
1: Perpetuum Ebner Musical 1
2: A Time to Kill and a Time to Heal
3: Preparation for Motor Drive
4: Motor control with Infineon Motor Shield and Arduino UNO
5: Turntable speed sample testbed with Arduino UNO
6: Turntable Speed Sensor design
7: Control Theory - End of Chapter 1
Chapter 2: First Enchantments
8: Digital Light Organ Enchantment
9: Autonomous Servo Lift
10: SMD Time - Solder the IR Speed Sensor PCB
11: Yelp - who can Help me to Compile and Run my First SAMA5D4 C Program
12: Son et Lumiere - End of Chapter 2
Chapter 3: Taming the Board
13: Breakthrough - Run my own C++ Program on the SAMA5D4
14: Digital Light Organ Input Buffer
15: SAMA5D4 Blinky
16: Scope Creep
17: Audio Sampling with 16-bit ADC ADS8343
18: Sending Files to SAMA5D4 over USB
19: Port my Light Organ from Arduino to SAMA5D4
20: Fast Fourier Transform on the SAMA5D4 - End of Chapter 3
Epilogue: Reaching for the Clouds
21: Right-Sizing my Plans
22: My Own C++ Buffered Sampler on the SAMA5D4
Interlude
23: Building In the Motorized Light Organ
24: Up to the Clouds with Yún
25: Publish or Perish
26: Turntable Finished
Stretch & Boni
Bonus 1a: Remote Light Organ with WiFI pt. 1
Bonus 1b: Remote Light Organ with WiFI pt. 2
Grande Finale: Paho MQTT Client on the SAMA5D4
Related blog
Vintage Turntable repair: Can I fix a Perpetuum Ebner from 1958
Review 1: Atmel SMART SAMA5D4 Xplained Ultra Unboxing and First Steps
Review 2: Atmel SMART SAMA5D4 Xplained Ultra - Building the Libraries from Source
Review 3: Digital Continuous Rotation (360°) Servo Part 1
Review 4: Digital Continuous Rotation (360°) Servo Part 2
Review 5: Atmel SMART SAMA5D4 Xplained Ultra - TCP/IP running
Review 6: Atmel SMART SAMA5D4 Xplained Ultra - LINUX Distro with SSH support
poem
Enchanted Objects: Let's work together to tame the ATMEL SMART SAMA5D4 Xplained Ultra kit
17 bis: Off South...
Review 7: Atmel SMART SAMA5D4 Xplained Ultra - C++ ADC Example on Linux
Review 8: Atmel SMART SAMA5D4 Xplained Ultra - Product Review
Review 9a: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 1
Review 9b: Atmel SMART SAMA5D4 Xplained Ultra - Set up ADC Buffer with Hardware Trigger Part 2
Review 10: Atmel SMART SAMA5D4 Xplained Ultra - New Content on AT91.com
1958 Turntable from the Black Forest - Summary of the Enchanted Player Story

I don't know if it was already there for a longer time, or that I missed it before,

Yesterday and today I found significant new and interesting content for our SAMA5D4 board.

 

 

The content is available from the Linux4SAM home page, and from the side menu. There's a step-by-step explanation on loading a bootloader and binary to the board, including screen captures.

And there's a new release of the Linux demos, 4.7. I've installed that yesterday and the Linux distro works flawlessly. I'm using the board headless, so I haven't run any of the graphical demos.

 

The image loaded very smoothly.

I just had to adapt demo_linux_nandflash.bat to match the COM port that's assigned on my PC (I first started SAM-BA in GUI mode to check the COM name it's expecting..

 

sam-ba.exe \USBserial\COM6 at91sama5 .....




 

The board started up perfectly. Impressed.

 

Now for me it's back to finding out if the distro contains the ADC driver, and what it's name is.

I'm trying the Buffer and Hardware Trigger  capabilities of the SAMA5D4.

I follow the instructions of http://www.at91.com/linux4sam/bin/view/Linux4SAM/IioAdcDriver.

 

In Part 1, I enabled the hardware trigger from the linux prompt.

In this post, I'm testing the buffer with the test tool generic_buffer.

 

Building the Test Tool

 

generic_buffer is available as c source. There's a number of them available in different versions on the internet, but not all compile for this board (or for my tool chain).

I steered away from examples that use the be16toh function. I got errors when I compiled those.

There are also versions without that function.

I used (without further analysis, just because it was the first hit) kernel-rsbac-source-3.0.2-1mdv-1-1-mdv2011.0.i586.rpm.59587983/usr/src/3.0.2-rsbac-1mdv/drivers/staging/iio/Documentatio…

Use the one from the atmel git: https://raw.githubusercontent.com/linux4sam/linux-at91/master/drivers/staging/iio/Documentation/generic_buffer.c

 

Run the Test Tool

 

My first run is with the analog input connected to ground. I'm doing the exercise while writing the blog, so what you see here is exactly as it happens.

 

Photo 21-05-15 21 19 47.jpg

 

As a first step, I load my compiled program

 

rz -e -b -X generic_buffer
chmod +x generic_buffer
















 

and execute the steps documented in part 1:

 

modprobe at91_adc
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en
echo "fc034000.adc-dev0-continuous" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
echo 100 > /sys/bus/iio/devices/iio:device0/buffer/length
echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable















 

And now the moment suprême: running that binary:

 

# Don't use at91_adc when working with a 'device tree' linux distro ./generic_buffer -n at91_adc -t fc034000.adc-dev0-continuous -l 2














 

./generic_buffer -n fc034000.adc -t fc034000.adc-dev0-continuous -l 2

 

where:

  • at91_adc is the IIO driver
  • fc034000.adc is the IIO driver
  • fc034000.adc-dev0-continuous is the trigger name
  • 2 is the buffer lenght (I would expect 100 here because I echo 100 to buffer/length a few lines higher. I'm following the advice of linux4sam here.

 

And here I fail. Failed to find the at91_adc

My error message is related to the driver. I'm posting a new post on the at91.com forum to see what's wrong.

 

hold on...

 

 

While my post is open on the at91 forum, I'm continuing with investigations.

 

Here's where the code fails:

    /* Find the device requested */
    dev_num = find_type_by_name(device_name, "device");
    if (dev_num < 0) {
        printf("Failed to find the %s\n", device_name);
        ret = -ENODEV;
        goto error_ret;
    }
    printf("iio device number being used is %d\n", dev_num);













 

I'm going to check how find_type_by_name() works, and check in the ii0 folders if I find something obvious...

update May 22:

 

Solved. The error was caused by 2 things:

 

(see element14 question SAMA5D4 ADC driver - do you know a Linux image that has the driver?)

 

1. the source for generic_buffer.c : 

There's a multitude of versions of that file on the internet.
Here's the correct one: https://raw.githubusercontent.com/linux4sam/linux-at91/master/drivers/staging/iio/Documentation/generic_buffer.c

 

In the correct one, here's how the device is found:

 

 

dev_num = find_type_by_name(device_name, "iio:device");









 

In the wrong one:

 

dev_num = find_type_by_name(device_name, "device");









 

 

2. With the switch of the Yocto distro to device tree, the name of driver/trigger changed.

 

This is reflected on http://www.at91.com/linux4sam/bin/view/Linux4SAM/IioAdcDriver

 

echo "fc034000.adc-dev0-continuous" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
./generic_buffer -n fc034000.adc -t fc034000.adc-dev0-continuous -l 2
The result:


 

Maybe the image that I use (the HDMI demo that peteroakes also uses in his videos) may not have the driver for ADC loaded...

 

According to at91.com, the driver should be at /lib/modules/3.5.0/kernel/drivers/iio/adc/at91_adc.ko

My build is not 3.5.0, but 3.10.0-custom.

I get up to lib/modules/3.10.0-custom/kernel/drivers.

There is no iio in that folder.

 

 

I'l check if there's more info somewhere else...

In /sys/devices/ahb/ahb:apb/fc034000.adc/modalias , I found a reference to the driver name at91sam9x5-adc

But I couldn't load the driver and the generic_buffer tool failed to find that driver too.

 

I've also downloaded nd installed the latet Poky image available from Sama5d4XplainedMainPage < Linux4SAM < TWiki

I've done a file find for all .ko files (I'm searching where the adc_91 would b).

 

find . -print | grep -i '.*[.]ko' > ~/drivers.log










 

The results are attached to the blog (drivers.log). I couldn't find the adc driver.

I'm trying the Buffer and Hardware Trigger  capabilities of the SAMA5D4.

I followed the instructions on IioAdcDriver < Linux4SAM < TWiki, but was struggling with one particular command.

I raised a topic on tha at91.com forum. And resolved it myself.

Sometimes writing down your problem leads to the solution.

 

Instructible

 

You can read my topic on the at91 site to see where I was in trouble.  (jc: At the time of posting this blog, I didn't know yet that I would get the at91.com treatment)
In this blog, I'll post the how-to.

 

Poky (Yocto Project Reference Distro) 1.6.1 sama5d4-xplained /dev/ttyS0


sama5d4-xplained login: root
root@sama5d4-xplained:~# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en
root@sama5d4-xplained:~# echo "fc034000.adc-dev0-continuous" > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
root@sama5d4-xplained:~# echo 100 > /sys/bus/iio/devices/iio:device0/buffer/length
root@sama5d4-xplained:~# echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable
root@sama5d4-xplained:~#







 

This will start the process. The ADC will now fill the character device /dev/iio:device0 with data.

 

How did I get the magic name "fc034000.adc-dev0-continuous"? It's different than the name "at91_adc-dev0-external" used in the AT91.com instructions.

By fiddling around a lot, and then by submitting the command

 

 

root@sama5d4-xplained:/sys/bus/iio/devices/trigger0# cat name
fc034000.adc-dev0-continuous







 

I still have to write a program that reads the character device.

I have already checked the source of drivers/staging/iio/Documentation/generic_buffer.c but I haven't been successful in compiling it yet.

I'll also need to learn how I can set the sampling speed.

I'd like to get 128 samples in 3.86ms. Check my previous post to understand why.


I hope that by putting this example here, I can help fellow developers started with the on-board ADC.