Recording data from two adxl345 sensors

F_Dos's icon

Hi,

I'm working on a tree vibration installation and receiving live data from two ADXL345 accelerometers via the serial object in Max/MSP.

The data comes in as six float values per reading:

x1, y1, z1 (sensor 1)

x2, y2, z2 (sensor 2)

I'd like to record this data and play it back later for prototyping the sonification without needing the physical sensors connected.

Two options I'm considering:

1. Record and playback directly inside Max

2. Save to CSV and read it back in Max later

What would be the recommended approach for this kind of multi-channel float data? Any patches or objects you'd suggest?

TFL's icon

You could do all in Max:

  • Receive data from serial in Max

  • Parse data to extract your 6 floats

  • Either:

    • timestamp each set of x1 y1 z1 x2 y2 z2 value with a delta time between current and previous event ([timer] is perfect for that), and record that data into [coll] or [text] that you can then write to disk. You'll then need to develop your own reader based on a [delay] and a [counter] requesting a new line after waiting for the delta time recorded in the current line

    • use [mtr] to automatically timestamp and record incoming data, write it to disk and provide a way to replay it

    • if you want to go extra fancy, use [mubu.record] from the MUBU package, which can be quite difficult to set up if that's the first time you use this package, but then allows you to easily store, replay at various speeds, display, edit, analysis, that data.

F_Dos's icon
  • use [mtr] to automatically timestamp and record incoming data, write it to disk and provide a way to replay it

How is possible to record it into file?

Source Audio's icon

You have done this before , why ask again ?

TFL's icon

How is possible to record it into file?

See the "Storage" tab of [mtr] help file.

F_Dos's icon

You have done this before , why ask again ?


I record into a txt file, never playback a file. but I will look into mtr which seems pretty straightforward

Source Audio's icon

if you record into coll at 1ms or any other interval you can also play it back.

using same clocker as for recording, only omit store

F_Dos's icon

Is this the right way? (its from example you showed me a year ago)

the problem is that if the first index values is not changing but the rest are the data is not recorded. In that case I should use join @triggers -1 instead?

Max Patch
Copy patch and select New From Clipboard in Max.

Source Audio's icon

If I remember correctly, you refused to capture data only when data changed

and bloated coll with mass of same values.

even so, you can play coll using same clock as for recording.

if you were inserting data only on change still using 1ms interval, same thing.

is that so complicated ?

F_Dos's icon

I added an automatic storing of the data into a folder. Is that make sense?

Max Patch
Copy patch and select New From Clipboard in Max.



why in every data file the last line from the previous recording is in the first line?

F_Dos's icon

another question - When I'm doing read to the coll object and select some old saved file and then pressing playback it won't play the actual last file or at least not is actual length.

Source Audio's icon

you must have some leftovers if you get things like

last line of previous rec in new rec.

you have to wipe all from whatever you use to collect data,

before starting new recording.

And also reset clocker time.

I can not answer your last question,

how should I know what is in that coll file ?

Source Audio's icon

I had to remove all but parts in question.

This should give you clean recording without leftovers.

F_Dos's icon

Thanks for that! but how to record this into a text file in a specific folder? The example I put was not correct?

If I remember correctly, you refused to capture data only when data changed

This is true, but it was for different project. This one I don't mind to record only on value change

Is this valid system for recording the coll data?

Max Patch
Copy patch and select New From Clipboard in Max.

How to make the playback stops when data reached the end of the file?

How to be able to load and playback a previous recorded file? (from that system)

Source Audio's icon

Your patch had many mistakes.

starting with setting session date-time

and other things.

you must :

1. start session = set session date and time, enable record button

2. start recordings, add take number and write file when rec done

3. finish session = disable record button, reset timer and whatever else is needed

your write file maybe did not work because folder you specified did not exist.

you must either make that folder in advance,

or check if it exists by passing it through absolutepath, and if it does not exist

use something like shell to create it.

all that should be allready known to you, so I hope

your next question is not "how do I do that... "

F_Dos's icon

I create the folder in advance, it was suppose to be something simple, just to save some data on my local computer. I of course make it over complicated...

Thanks for the above suggestion.

Source Audio's icon

just in case you need to create folders, including subfolders.

regexp has 8 backslashes, needed to produce 2 backslashes out of 1 slash.

regexp / @substitute \\\\\\\\

F_Dos's icon

Hi,

I'm receiving live XYZ data from an ADXL345 accelerometer via serial into Max/MSP. The values are normalized between 0 and 1.

I'm looking for suggestions on how to visualize this data in real time — something that reflects the movement and vibration of the sensor

F_Dos's icon

Another question - when I press the read message to load a recorded txt file Max is crashing. Why is that? What I'm doing wrong?

Max Patch
Copy patch and select New From Clipboard in Max.

file I try to load for example:

0_20260712_1434.txt
txt 2.77 MB


Source Audio's icon

your coll has strange capture :

270 lines with single int, then the rest with 4 floats.

but anyway, my coll does not crash if I read that text file in it.

Maybe because I use Max 8 ?

or you do something else while reading that file ?

and for sure - you capture lines into coll, no matter if something changed at input or not ....

F_Dos's icon

Maybe because I use Max 8 ?

I'm using 9.

or you do something else while reading that file ?

I closed the serial while reading and it stil crashing..

F_Dos's icon

So I downloaded max8 and it is indeed not crash but also not playing...

edit2: I close and reopen Max9 and now it is not crashing... Not sure what it was

F_Dos's icon

Any recommendation regarding the visualisation of the data?

another question - Is there a way to "translate" this ADXL345 data of x y z into frequency?

ESP32S3 code:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
#include <WiFi.h>
#include <esp_now.h>

Adafruit_ADXL345_Unified accel1 = Adafruit_ADXL345_Unified(1);

const float THRESHOLD           = 0.01;
const float RANGE               = 2.0;
const float ALPHA               = 0.2;
const int   CALIBRATION_SAMPLES = 100;

uint8_t receiverMAC[] = {0x68, 0xEE, 0x8F, 0x46, 0x4E, 0x34};

typedef struct {
  float x;
  float y;
  float z;
} SensorData;

SensorData dataToSend;
bool sensor1ok = false;

struct SensorState {
  float centerX = 0, centerY = 0, centerZ = 9.8;
  float prevX   = 0.5, prevY = 0.5, prevZ = 0.5;
  float filtX   = 0.5, filtY = 0.5, filtZ = 0.5;
};

SensorState s1;

void onSent(const wifi_tx_info_t *info, esp_now_send_status_t status) {}

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  delay(1000);

  // Init ESP-NOW
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init failed"); while(1); }
  esp_now_register_send_cb(onSent);

  // Add receiver peer
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;
  if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println("Failed to add peer"); while(1); }

  Wire.begin();
  scanI2C();
  initSensors();
  calibrate();
  blinkLED(3, 200);
  Serial.end();
}

void loop() {
  heartbeat();
  if (sensor1ok) readAndSend(accel1, s1);
  delay(20);
}

float normalize(float value, float center) {
  float n = 0.5 + (value - center) / (2.0 * RANGE);
  if (n < 0.0) n = 0.0;
  if (n > 1.0) n = 1.0;
  return n;
}

void readAndSend(Adafruit_ADXL345_Unified &accel, SensorState &s) {
  sensors_event_t event;
  accel.getEvent(&event);

  float nx = normalize(event.acceleration.x, s.centerX);
  float ny = normalize(event.acceleration.y, s.centerY);
  float nz = normalize(event.acceleration.z, s.centerZ);

  // Low pass filter
  s.filtX = ALPHA * nx + (1 - ALPHA) * s.filtX;
  s.filtY = ALPHA * ny + (1 - ALPHA) * s.filtY;
  s.filtZ = ALPHA * nz + (1 - ALPHA) * s.filtZ;

  bool changed = false;
  if (abs(s.filtX - s.prevX) > THRESHOLD) { s.prevX = s.filtX; changed = true; }
  if (abs(s.filtY - s.prevY) > THRESHOLD) { s.prevY = s.filtY; changed = true; }
  if (abs(s.filtZ - s.prevZ) > THRESHOLD) { s.prevZ = s.filtZ; changed = true; }

  if (changed) {
    // Send via ESP-NOW
    dataToSend.x = s.filtX;
    dataToSend.y = s.filtY;
    dataToSend.z = s.filtZ;
    esp_now_send(receiverMAC, (uint8_t *)&dataToSend, sizeof(dataToSend));

    // Debug print
    Serial.print("X1 "); Serial.print(s.filtX, 3);
    Serial.print(",Y1 "); Serial.print(s.filtY, 3);
    Serial.print(",Z1 "); Serial.println(s.filtZ, 3);
  }
}

void calibrate() {
  Serial.println(F("Calibrating..."));
  float sx = 0, sy = 0, sz = 0;

  for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
    sensors_event_t event;
    if (sensor1ok) {
      accel1.getEvent(&event);
      sx += event.acceleration.x;
      sy += event.acceleration.y;
      sz += event.acceleration.z;
    }
    delay(20);
  }

  if (sensor1ok) {
    s1.centerX = sx / CALIBRATION_SAMPLES;
    s1.centerY = sy / CALIBRATION_SAMPLES;
    s1.centerZ = sz / CALIBRATION_SAMPLES;
    Serial.print(F("Center X:")); Serial.print(s1.centerX, 2);
    Serial.print(F(" Y:")); Serial.print(s1.centerY, 2);
    Serial.print(F(" Z:")); Serial.println(s1.centerZ, 2);
  }

  Serial.println(F("Ready."));
}

void blinkLED(int times, int ms) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_BUILTIN, HIGH); delay(ms);
    digitalWrite(LED_BUILTIN, LOW);  delay(ms);
  }
}

void heartbeat() {
  static unsigned long last = 0;
  unsigned long now = millis();
  if (now - last >= 500) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    last = now;
  }
}

void scanI2C() {
  Serial.println(F("Scanning I2C..."));
  int found = 0;
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.print(F("  Found: 0x"));
      Serial.println(addr, HEX);
      found++;
    }
  }
  if (found == 0) Serial.println(F("  No I2C devices found"));
  Serial.println(F("---"));
}

void initSensors() {
  sensor1ok = accel1.begin(0x53);
  if (sensor1ok) {
    accel1.setRange(ADXL345_RANGE_2_G);
    Serial.println(F("Sensor 1 OK"));
  } else {
    Serial.println(F("WARNING: Sensor 1 not found"));
  }
}
TFL's icon

For visualization, try [multislider] with size set to a high value and setstyle to 2 (point scroll) or 4 (reverse point scroll).

Another solution could be [zl.stream] >[jit.fill] > [jit.matrix] > [jit.pwindow].

You could go fancier with jit.gl objects or jsui/v8ui/jspainterfile, but I would need further detail about the kind of visualization you are after.

F_Dos's icon

My max9 still crashing when I read the coll file, Anyone with max9 experience the same?

F_Dos's icon

I have added a delay bang of 1000ms and now it crashed after 1000ms. So it means the problem is somewhere in the [p playback sys] ?

Source Audio's icon

I don't know what is in that playback patch, but you don't read that long text files into coll while using playback or ?

you can also simplify your life and add another coll for playback

F_Dos's icon

I don't know what is in that playback patch, but you don't read that long text files into coll while using playback or ?

Max Patch
Copy patch and select New From Clipboard in Max.

you can also simplify your life and add another coll for playback


What do you mean? why and how? Do you think this is why is crashing?

TFL's icon

My max9 still crashing when I read the coll file

Try to put a [defer] or [deferlow] just before your read message reaches [coll]. There is a chance it comes from the scheduler thread, which sometime can cause crashes with read/write operation since those are asynchronous. You can also use [threadcheck] to know in which thread your read message lives in.

F_Dos's icon

Try to put a [defer] or [deferlow] just before your read message reaches [coll]. There is a chance it comes from the scheduler thread, which sometime can cause crashes with read/write operation since those are asynchronous

Both did not solved it

Source Audio's icon

You either read file into coll, or you capture into it or play from it.

separate that 2 processes, STOP capture and playback before you trigger read message.

by the way I had a short look into that patch - you can not place grab into subpatch and link it outside, at least not in Max 8

F_Dos's icon

separate that 2 processes, STOP capture and playback before you trigger read message.


Max Patch
Copy patch and select New From Clipboard in Max.

Like this? it seems to work

by the way I had a short look into that patch - you can not place grab into subpatch and link it outside, at least not in Max 8

OK so maybe this what make it to crash? I will try again soon without the subpatch

update: I try without the subpatch and it working now without crashing. That was probably it

F_Dos's icon

If I want to jump to a specific time in the playback (lets say the total time is 02:35 min and I want to start from 01:35 - I could just and that value in ms to the count reset input?

Source Audio's icon

You can anyway use 1 time object, maybe even better one clocker or metro 1 for both recording and playback, and add switch to toggle it between that 2 functions.

and to offset the playback, I would not change counter reset, but add at it's output.

Sorry that I don't look at your patchers, it makes me a bit nervous, because I posted so many times working and simple code, and you manage to overcomplicate and destruct it most of the time.

F_Dos's icon

Thanks!!!//!!!!

F_Dos's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Edit: I forgot one connection.

Do you see something I'm missing? I try to record few seconds and when I press the playback toggle (after I stopped the recording) it won't playback the data recorded)

F_Dos's icon

So it play the data back but it wont stop when arrive to the last value in coll.

it seems the grab object output the last value instad of the time of that last value in coll

F_Dos's icon

ok I needed to use the middle outlet of the grab object

F_Dos's icon

For visualization, try [multislider] with size set to a high value and setstyle to 2 (point scroll) or 4 (reverse point scroll).

Another solution could be [zl.stream] >[jit.fill] > [jit.matrix] > [jit.pwindow].

You could go fancier with jit.gl objects or jsui/v8ui/jspainterfile, but I would need further detail about the kind of visualization you are after.

Thanks thw first suggestion works great! the second one did not work for me.

Regarding the first one:

how can I reset all values to 0? the message min did not work for me

how can I change the direction of the data flow in the multislider so new value will be on the right hand side?

Is there a way to take those 3 value x y z and make it into 3d visuality?

Max Patch
Copy patch and select New From Clipboard in Max.

TFL's icon

how can I reset all values to 0?

Go through the messages accepted by the object, there are a couple ways to reset it in scrolling mode.

how can I change the direction of the data flow in the multislider so new value will be on the right hand side?

I gave you the answer in my suggestion already. There are two scrolling mode. The second one is reversed.

Is there a way to take those 3 value x y z and make it into 3d visuality?

You could superimpose three multisliders with transparent backgrounds, if that's what you mean. If you want an actual 3D view of the points history in space you'll need something more fancy.

Source Audio's icon

rec end - get last coll index, and yes that with grab was my mistake

F_Dos's icon

the output of split go inside coll?

where this gray line to the right of coll go to/come from?

Max Patch
Copy patch and select New From Clipboard in Max.

The time present when the record stop is not the actual time

F_Dos's icon

I gave you the answer in my suggestion already. There are two scrolling mode. The second one is reversed.

True. Thanks. I change to that now.

Go through the messages accepted by the object, there are a couple ways to reset it in scrolling mode.

I try message min - did not work. I try set 0 but it zero only the last value. I try with uzi and it will reset only the last value. Not sure what am I missing

Source Audio's icon

you take it wrong, when record stops it triggers end, bang message to get last RECORDED INDEX

same as when you do so after reading file into coll.

here is the patch.

Max Patch
Copy patch and select New From Clipboard in Max.

One should also limit offset to available length minus something ...

F_Dos's icon

I understand the confusing I have. Here is example:

ms after record stopped:

ms in grab object after pressed stop recording:

numbers are not the same. is this because at 10407 it was the last time value has changes.

This is why I got two different numbers:

I just now saw you shared the patch. Thanks.

So if to make my question clear without all those screenshots - how to get last index after records ends but the actual time when button pressed. Is that even possible with the current system?

F_Dos's icon

Another small question - Assuming a recording of x time long - is there a way to report at what segments in that recording there was more then 5 seconds of no change in data?

so if between 6000 to 65700 that data stay the same it will be reported, as well if it happens between 7800 to 17800.

but if there was same data between 0 to 4998 it won't report that

TFL's icon

I try message min - did not work. I try set 0 but it zero only the last value. I try with uzi and it will reset only the last value. Not sure what am I missing

My bad, there is one easy way, not two. But still listed in the available methods:



F_Dos's icon

My bad, there is one easy way, not two. But still listed in the available methods:

Great thanks!

Source Audio's icon

My advice - do not capture or better say insert lines into coll if no change at input.

"how to get last index after records ends but the actual time when button pressed. Is that even possible with the current system?"

last index is last inserted time, button pressed (if you mean record end) is recording time end.

both have no relation at all , unless you put also unchanged values into coll.

To your other question, you can measure time between events

and if it is > 5000 bang. here one example, triggers at each input

but what would you do with that bang ?

Same if you read captured file into coll, you could dump coll, measure delta between indexes and

make a list with all positions of unactivity > 5000 ms.

F_Dos's icon

measure delta between indexes and

make a list with all positions of unactivity > 5000 ms.

How to measure delta after dumping coll?

last index is last inserted time, button pressed (if you mean record end) is recording time end.

Yes I mean the record time end. I could just run another timer that measure record start/stop time. Thanks.

To your other question, you can measure time between events

and if it is > 5000 bang. here one example, triggers at each input

but what would you do with that bang ?

Nothing in praticulary I wanted to know at what time of the recordings I have areas of no new data. So if I started the recording at 7PM and I recorded for two hours I want an easy way to see at what time in that two hours frame I had segments of time with no change of data what so ever

Source Audio's icon

no need for any timer, you know you start recording at time zero, on record stop grab that number and send it where you want.

here dual reporter

Max Patch
Copy patch and select New From Clipboard in Max.

you get bang as soon as no data for 5 sec, or measure time between data.

to dump coll simply connect it to t i i and it will give you every interval.

you grab > 5000 ones

F_Dos's icon

Thank you.

I'm sorry of not getting it so fast. I'm not sure I understand how to connect it with the previous system?

to dump coll simply connect it to t i i and it will give you every interval.

You mean to connect the output of coll to t i i ?

Max Patch
Copy patch and select New From Clipboard in Max.