Recording data from two adxl345 sensors
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?
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.
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?
You have done this before , why ask again ?
How is possible to record it into file?
See the "Storage" tab of [mtr] help file.
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
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
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?

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 ?

I added an automatic storing of the data into a folder. Is that make sense?
why in every data file the last line from the previous recording is in the first line?



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.

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 ?
I had to remove all but parts in question.
This should give you clean recording without leftovers.

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?
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)
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... "
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.
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 \\\\\\\\

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
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 ....
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..
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
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"));
}
}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.
My max9 still crashing when I read the coll file, Anyone with max9 experience the same?
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] ?

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
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
What do you mean? why and how? Do you think this is why is crashing?
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.
Try to put a [defer] or [deferlow] just before your
readmessage 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
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
separate that 2 processes, STOP capture and playback before you trigger read message.
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
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?

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.

Thanks!!!//!!!!
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)
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


ok I needed to use the middle outlet of the grab object
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?
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.
rec end - get last coll index, and yes that with grab was my mistake

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

The time present when the record stop is not the actual time
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
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.
One should also limit offset to available length minus something ...
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?
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
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:

My bad, there is one easy way, not two. But still listed in the available methods:
Great thanks!
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.
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
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

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
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 ?