arduino/maxuino sinewave generator on digital output

Tang Frere's icon

Hi,

does anyone knows if it is possible to generate frequencies on digital outputs of the arduino (with a piezzo) in max/msp?
I know it's possible with "tone ()" function in arduino IDE but I can not find a way to do it in maxuino. The idea is to generate multiple frequencies on several digital output of the arduino.

thank you,

best

Source Audio's icon

My advice is to drop maxuino and any connection kits
or similar and make simple max patch and arduino sketch
to do what you want.
depending on number of piezos connected, you might need
TimerFreeTone() library instead of tone()

https://bitbucket.org/teckel12/arduino-timer-free-tone/downloads/
-----------
Since few years firmataplus exists, aiming to enable use of tone library,
but you won't find any trace of that in outdated maxuino.
There are also not really well documented maxfirmata externals
https://github.com/NullMember/MaxFirmata

and so on...

Again, dropping firmata would save you a lot of time, and at the end
maybe you'll get what you want.

Iain Duncan's icon

+1 to that, it's really not hard to roll your own. "Exploring Arduino" is a great book.

Tang Frere's icon

Thank you for your answers! I'm looking at all of these. The idea is to create a composition with a lot of piezos and I can not see how to realise it directly with Arduino IDE. (I know "tone()" is pretty easy to use but seems a bit limited)
For sure it s possible to do it in max, even without maxuino. But can't see how for the moment.

Iain Duncan's icon

If you're not set on Arduino, it might be worth looking at the Daisy platform, on which you can run Gen~ exports.

Tang Frere's icon

Hi guys,
I drop firmata/maxuino to use instead the [serial] object in max and the "TimerFreeTone" library of arduino. Obviously it seems much more relevant... Thanks for the advices !

I'm scratching my head now with how to send multiples variables from max to arduino. I manage to send one only with [serial] in max and with the "Serial.read" function in arduino.

I hope I'm clear enough with my explainations. Do you know how to send/receive multiples values FROM max TO arduino (I found a great tutorial about how to do the oposite but nothing about this way...)

thank you,

Source Audio's icon

the optimal way to send values from max to arduino would depend
on your plan with piezos.
I mean if all would receive freq to play at same time, having same duration and volume ?
Or individual, which again could in maximum have all 3 values included,
freq, duration , volume.
here is one example using prepended IDs for 6 arduino pins,
followed by 3 values.

#include <TimerFreeTone.h>
byte inByte;
void setup() {Serial.begin(57600);} // increased baud rate if really fast data needed

void loop() {if (Serial.available() > 0) {inByte = Serial.read();
if (inByte == 'A'){int freq1 = Serial.parseInt(); int dur1 = Serial.parseInt(); int vol1 = Serial.parseInt(); TimerFreeTone(3, freq1, dur1, vol1);}
if (inByte == 'B'){int freq2 = Serial.parseInt(); int dur2 = Serial.parseInt(); int vol2 = Serial.parseInt(); TimerFreeTone(4, freq2, dur2, vol2);}
if (inByte == 'C'){int freq3 = Serial.parseInt(); int dur3 = Serial.parseInt(); int vol3 = Serial.parseInt(); TimerFreeTone(5, freq3, dur3, vol3);}
if (inByte == 'D'){int freq4 = Serial.parseInt(); int dur4 = Serial.parseInt(); int vol4 = Serial.parseInt(); TimerFreeTone(6, freq4, dur4, vol4);}
if (inByte == 'E'){int freq5 = Serial.parseInt(); int dur5 = Serial.parseInt(); int vol5 = Serial.parseInt(); TimerFreeTone(7, freq5, dur5, vol5);}
if (inByte == 'F'){int freq6 = Serial.parseInt(); int dur6 = Serial.parseInt(); int vol6 = Serial.parseInt(); TimerFreeTone(8, freq6, dur6, vol6);}
}}

// SYNTAX:
// TimerFreeTone( pin, frequency, duration [, volume ] ) - Play a note on pin at frequency in Hz for duration in milliseconds.
// Parameters:
// * pin - Pin speaker is wired to (other wire to ground, be sure to add an inline 100 ohm resistor).
// * frequency - Play the specified frequency (should work fairly well in the 100 to 15000 Hz range).
// * duration - Set the duration to play in milliseconds. Range: 0 to 65535 (65.5 seconds).
// * volume - Optionally set the tone volume level (from 1 to 10), defaults to full volume (10).

maxpatch

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

--------------
one could remove volume if you don't need that.

if you would prefer to send one long list containing only frequencies
which would be distributed to pins with fixed duration and volume,
things would look a bit more simple.
But then all would sound at same time....

Tang Frere's icon

Hi, thanks a lot for this. It seemed clear enough for me until... I tried it.

It does not work and I don't know why. The arduino led is reacting but nothing comes from the piezo.
(The wire is ok, I have sound in other ways).
I'm not sure to understand the role of [atoi] but I have to work on it.

If I understand your patch example I must have a 220hz sound during 250 miliseconds at the maximum (10) volum on the pin 3? (Refer to the first message object).

thanks again for your time!

Source Audio's icon

Sorry to have troubled you with that mistake.
In Arduino code I used Serial.parseInt(); for all values
but in Max in pack 0. 0 0 I used float for the frequency,
which should be so.
So please simply replace all
int freq1 = Serial.parseInt();
int freq1 = Serial.parseFloat();
etc.
time and vol should keep parseInt
--------
I guess you know that all tone() based libs are mono,
means no matter how different outputs you use, at the end
only one sound can be generated at a time.

Tang Frere's icon

Hi,
of course, it seemed to be the solution but still nothing is hapening...
One more problem is that, if max is runing, I can not open the serial port of the arduino to print the values : it says "port busy"...
This is not helping us to see what is hapening

Tang Frere's icon

oh, and I changed "int freq1 = Serial.parseFloat();" with "float freq1 = Serial.parseFloat();"
but still nothing

Source Audio's icon

send close message to serial object in max to
close the port and allow arduio to upload code

Tang Frere's icon

Ok i found the point : it's (inByte = 'A') instead of "=="
Wow thank you very much

Source Audio's icon

yes, that was my typo in last message, sorry.

To reply to your idea of using both max and arduino serial monitor
at same time - that is not possible.
Only one application can use selected serial port at a time.

If you really need to monitor serial data, you can send it back to
Max and parse it here, but in so simple code it is really not needed.
--------
I must say that I am dissapointed with timerfreetone() because I
expected it to be multytimbral capable, playing more simultaneous voices.
But it is not the case, because it uses delaymicroseconds() to generate pulses,
which puts generated tones in a queue, waiting for first to end, before playing next.

At the moment, for "usual" arduinos one can produce as many voices as
there are available hardware timers, using differnt Tone libs,
and that depends on the processor in use.

ESP32 boards should be able to play 16 voices to 16 piezos
using ledcWriteTone(pin, frequency);
or using tone32 library

https://www.thomascountz.com/2021/02/21/arduino-tone-for-esp32
https://github.com/lbernstone/Tone32
---------
But if you only need to place mono voice into several piezos,
you can do with simple tone or timerfree version.
would work the same, and also having volume option might be handy.

Tang Frere's icon

Hi,

Just to answer about this (even if it seems to be another topic):
" ...using both max and arduino serial monitor at same time - that is not possible. Only one application can use selected serial port at a time. "
This guy is doing it very easily in his tutorial :
https://www.youtube.com/watch?v=68L-WHh3Ows

Back to the topic in hand, actually the "==" is used here and is working fine :
http://www.noisemantra.com/Physical%20Computing/arduino-MaxMSP%20communications%2011.pdf
So I'm a bit confused of what we have to use. But anyway, I'm still having the same problem : I want to make several sounds at the same time so tone() or FreeTimerTone() are not helping.

Therefore, it is not possible to have simultaneous voices with a classic arduino board. The ESP32 seems to be the one but only for 16 voices. So why not with several EPS32 for more but this is another story...

thanks again, I'm still hoping to find a way to do what I want!

Source Audio's icon

Older Arduino versions (< 1.6.5) display error and refuse to communicate with the board if another app is using it.

New Arduino version throws no error, and pretends as if it were ok,
but in case other app is using same port, data will be interrupted, and code upload
will fail.
Same way, if serial monitor is open, and one tries to activate port in Max
max will receive no data.
If Max was first using the port, activating serial monitor in arduino
will take over, but data to max will suffer, getting slow and interrupted.
---------
So much about looking you tube tutorials.

then, = and == are 2 different things
if a == b is comparison
a = b sets a to b
---------
If you want to use more than 16 piezos,
or better say polyphony for the composition,
it looks you need several boards similar to ESP32.
Or some clever voice management.
If you need more independent piezos, but 16 voices polyphony would be enough,
one could use I/O expanders like 74HC4067 to route pwm output to several
destinations.

Tang Frere's icon

Ok, I ll check all of these options.
Don't know how all of this would work with several boards plugged in, but should be achievable.
Let's try first it with one ESP32 so!

(I have to say I'm a bit surprised that nothing about similar projects have been already done)

thanks


Source Audio's icon

I am actually not surprised no such thing was developed
or at least published, it is too complicated for many channels
as it would envolve several boards or external hardware
and probably buzzer sound was not really interesting enough for such complexity.

Back to ESP32, I did not see a direct option to adjust also volume,
but one could do so by writing duty cycle together with frequency.

ledcWriteTone(pin, frequency); ledcWrite(pin, duty); // duty = volume

set PWM_RESOLUTION to 8-bit 0-255, (or 7-bit 0-127 to match midi notes velocity)

-----
Maybe that way one could translate midi note-velocity
into voice - freq - vol using poly in Max