
Когда я впервые столкнулся с Arduino, терменвокс казался мне сложным устройством, состоящим из сенсора и исполнительного устройства. Но он оказался весьма доступным и интересным в создании. У него простое строение: лишь один фотодатчик и переделанные наушники. После нескольких экспериментов я понял, что необходима функция самокалибровки, если я хочу неизменные характеристики на различных уровнях освещённости. Также, я решил сделать автонастройку с помощью математики, а не таблицы поиска. Для меня, пентатонная настройка оказалась самой подходящей, хотя её можно легко заменить на хроматическую или другую настройку по желанию.
Вот список того, что я использовал:
*Arduino Uno (запущено на Mac OS X 10.7.4 Intel)
*Кабель USB
*Макетная плата
*Копеечные наушники-вкладыши
*Стандартный фотодатчик
*Резистор на 10 кОм
*Немного проволоки
*Паяльник и припой
Шаг первый. Переделываем наушники.

Для того, чтобы посылать сигналы в наушники, я обрезал провода и припаял их к паре проволочных перемычек. Но здесь есть несколько нюансов.
Для начала, когда вы перережете провода и снимете часть изоляции, вы увидите четыре провода, по два с каждой стороны. Каждая пара образует цепь с одним наушником, поэтому мы можем считать, что каждая пара состоит из плюсового провода (обычно окрашены в красный или голубой цвет) и заземляющего провода (обычно меднокрасный). Мы можем использовать или только один наушник (следовательно, только одну пару проводов), или объединить провода для использования обоих наушников, тем самым обеспечивая в два раза больше веселья. Для этого мы припаяем плюсовые провода из каждой пары к одной проволочной перемычке, а заземляющие провода – к другой. При необходимости смотрите фото выше.
Во-вторых, под заметной резиновой изоляцией часто можно встретить слой прозрачной резины, обволакивающий четыре провода по отдельности. Простейший способ избавиться от него – нагревать провод зажигалкой до тех пор, пока резина не сгорит. Обратите внимание: после того, как это будет сделано, будет очень трудно различить цвета. Поэтому рекомендую оставить небольшой кусок провода необожжённым для того, чтобы можно было определить тип провода. Также, я использовал мокрую ткань для того, чтобы стереть остатки сожжённой смолы для улучшения проводимости.
У меня было очень простое оборудование для пайки, поэтому я просто скрутил провода наушников вместе и вокруг проволочной перемычки, а затем капнул чуть-чуть припоя.
Шаг второй. Собираем цепь.

Здесь у нас две простые цепи:
1. Цепь наушников: Используем любой цифровой выходной контакт Arduino и заземлитель для создания цепи с проволочными перемычками наушников.
2. Цепь датчика: Это стандартный делитель напряжения, который позволяет меняющемуся сопротивлению фотодатчика регулировать напряжение, считываемое аналоговым входным контактом Arduino. Для начала, я сделал последовательную цепь, которая идет по порядку: Arduino 5V контакт питания -> фотодатчик -> резистор на 10кОм ->заземление. Я опробовал напряжение в узле между резистором и фотодатчиком, используя аналоговый вход A0.
Иногда у меня были проблемы с качеством соединений. Я думаю, что лучше использовать более качественные проволочные перемычки в следующий раз. Когда что-то шло не так, я просто шевелил некоторые провода и определял качество соединения. Более хорошее закрепление провода в макетной плате или повторный обжиг провода всегда срабатывали.
Шаг третий. Пишем код.
Код состоит в основном из двух команд:
* команда AnalogRead, которая получает значение напряжения из вывода A0. Это эффективно измеряет уровень освещенности на фотодатчике.
* Команда tone, которая посылает сигнал к выводу 9 и заставляет наушники выдавать тон определённой частоты.
Конечно, есть ещё много вещей, которые нужно сделать:
* Установить контакт 9 как вывод.
* Цикл калибровки: пользователь показывает фотодатчику диапазон уровней освещения, обучая программу градуировать частоты.
* Измерить градацию и параметр сдвига на основе данных, полученных при калибровке.
* Автонастройка: округление частоты до ближайшего нужного тона с помощью логарифмов.
Вот сам код:
// Optical Theramin
//pin definitions
#define PHONES 9 // headphones connected to digital pin 9
#define PHOTOCELL 0 //photocell analog in pin 0
//variable definitions
long val = 0; //stores raw value from photocell
long maxread = 0; //maximum value from calibration phase
long minread = 1000; // minimum value from calibration phase
double f = 0; // frequency of sound
double normf = 0; // normalized frequency
double logf = 0; // logarithm of normalized frequency
int ilogf = 0; // rounded logarithm
int i = 0; // loop dummy variable
double factor = 0; // scaling factor for calibration
double shift = 0; // shift for calibration
long maxfreq = 1048; // maximum desired frequency after calibration
long minfreq = 131; // minimum desired frequency after calibration
//magic numbers that make the intervals sound pleasing
double gap = 1.148698355; //ratio of consecutive notes (pentatonic)
// it's the 5th root of 2
//double gap = 1.059463094; //ratio of consecutive notes (chromatic)
// its the 12th root of 2
void setup()
{
pinMode(PHONES, OUTPUT); // sets the digital pin as output
// calibration loop to determine a rasonable range of light levels (minread to maxread)
// and map that to frequencies between minfreq and maxfreq
for (i = 0; i< 500; i++) { // calibration loop runs for 5 seconds
val = analogRead(PHOTOCELL); // read photocell
tone(PHONES, val); // play raw tone to guide calibration
if (val > maxread) { // as the values climb, store the largest
maxread = val;
}
if (val < minread) { // as the values drop, store the smallest
minread = val;
}
delay(10); // reasonable delay
}
//Now we use the calibration to calculate scale and shift parameters
factor = (double)(maxfreq - minfreq) / (double)(maxread - minread); // scale parameter
//it's like a slope
shift = factor * minread - minfreq; //shift parameter: it's like an offset
}
void loop()
{
val = analogRead(PHOTOCELL); // read photocell
f = factor * val - shift; // this linearly maps the frequency to
// a value between minfreq and maxfreq
// according to the calibration result
normf = f / (double) minfreq; // Dividing an exponential function by the min value
logf = log(normf) / log(gap); // allows us to take the log (base gap) and the result
ilogf = round(logf); // is the number of notes above the lowest, once we round it.
f = minfreq * pow(gap,ilogf); // we better "unlog" it.
tone(PHONES, f); // this produces the tone signal
}
Шаг четвёртый. Запускаем.
Теперь просто открываете редактор Arduino, верифицируете код, подсоединяете USB и загружаете. Вот видеогайд:
Конечно, есть много возможностей для изменения и улучшения. Некоторые возможности включают в себя:
* Добавление регулятора громкости (реостат или другой фотодатчик)
* Использование лучшего динамика
* Сделать код более эффективным (меньше переменных типа double)
* Воспользовавшись функцией map вместо маппинга калибровки вручную
Не стесняйтесь предлагать свои идеи, и веселиться, раздражая людей своим терменвоксом :)
Это перевод статьи с сайта instructables.com.
P.S. Это мой первый перевод, поэтому прошу судить строго и сообщать об ошибках.