Измерение оборотов колеса с помощью датчика холла

renat56
Offline
Зарегистрирован: 04.04.2012

Всем Привет! Столкнулся с проблемой. Не могу расчитать обороты колеса на велосипеде, посредством аналагового датчика холла. Гуглил эту проблему, но встречаются примеры только для цифровых датчиков. Может кто то сталкивался с этим?

P.S. Строю такой девайс :                                                                                                                                           

Суть заключается в следующем, нужно что бы arduino на ходу расчитывала количиство оборотов в еденицу времени, после вычислям время одного оборота и подставляем в это значение в строку. Все это нужно для того что бы изображение на колесе оствалось неизменным т. е. (не растягивалось при большой скороти), что просходит при больших оборотах колеса.

Вот так это выглядит на колесе, пока что езжу так, что бы все отработать, а после планирую расширяться сдвиговыми регистрами, ну и естественно монолитной платой с atmel.

Diemon
Offline
Зарегистрирован: 18.11.2011

 Не уродуйте велосипед....., смотреть на него жалко :)

renat56
Offline
Зарегистрирован: 04.04.2012

Все в порядке, вечером неплохо смотрится) 

leshak
Offline
Зарегистрирован: 29.09.2011

 Не совсем понятно с чем у вас загвоздка:

  • С подключением и чтением датчика
  • С пересчетом его "сработок" в скорость вращения
  • В формировании "картинки" в зависимости от скорости вращения.

 

Ну и ссылочки на "цифровые примеры" можно дать. Что-бы лучше понять что вы имеете ввиду. И как подключаете (если проблемный первый пункт). И свой код дать. И описать "что происходит сейчас".

Еще можете погулилть (и тут, вроде было, и на хабре пробегало) "родственный проект", там делали часы из крутящегося степпера (или движок от винчестера использовали). То есть у них задача "определить скорость" - не стояла, они сами ее задавали, а вот логику "как формировать картинку в соотвествии с оборотами" - можно подсмореть.

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

 Не совсем понятно с чем у вас загвоздка:

  • С подключением и чтением датчика
  • С пересчетом его "сработок" в скорость вращения
  • В формировании "картинки" в зависимости от скорости вращения.

 

Ну и ссылочки на "цифровые примеры" можно дать. Что-бы лучше понять что вы имеете ввиду. И как подключаете (если проблемный первый пункт). И свой код дать. И описать "что происходит сейчас".

Еще можете погулилть (и тут, вроде было, и на хабре пробегало) "родственный проект", там делали часы из крутящегося степпера (или движок от винчестера использовали). То есть у них задача "определить скорость" - не стояла, они сами ее задавали, а вот логику "как формировать картинку в соотвествии с оборотами" - можно подсмореть.

С чтением датчка проблеи нету, в формировании картинки вроде разобрался, а вот с пересчетом его "сработок" в скорость вращения есть вопросы, так как датчик аналоговый в этом для меня сложность. 

Основеая проблема - это вычислить обороты. Потому что они в моем случае не постоянны. 

Вот нашел скетч для тахометра, думаю можно здесь применить? arduino.cc/playground/Learning/Tachometer

 

maksim
Offline
Зарегистрирован: 12.02.2012

Вам нужно не обороты считать, а время между срабатываниями датчика.

maksim
Offline
Зарегистрирован: 12.02.2012

Вы пробовали подключать ваш датчик к цифровому выводу и опрашивать его как цифровой?

renat56
Offline
Зарегистрирован: 04.04.2012

maksim пишет:

Вы пробовали подключать ваш датчик к цифровому выводу и опрашивать его как цифровой?

ну да, время)

Это ведь аналоговый датчик, так что подключить его к цифровому входу не представляется возможным.

leshak
Offline
Зарегистрирован: 29.09.2011

renat56 пишет:

 

С чтением датчка проблеи нету, в формировании картинки вроде разобрался, а вот с пересчетом его "сработок" в скорость вращения есть вопросы, так как датчик аналоговый в этом для меня сложность. 

 

Покажите код которым вы читаете. Непонято что же понимаете под его "аналоговостю" и что вы понимаете под "сработаками" (возможно по разному терминами пользуемся). В конечном итоге вам все нужно расматировать как "цифру". Сработал или не сработал. 0 или 1.

maksim
Offline
Зарегистрирован: 12.02.2012

 И что??? работает то он у вас как цифровой... магнитное поле или есть или его нет...
А так как он в своем составе имеет два транзистора, то с нормальным магнитом должен замечательно работать как цифровой.

leshak
Offline
Зарегистрирован: 29.09.2011

renat56 пишет:

Это ведь аналоговый датчик, так что подключить его к цифровому входу не представляется возможным.

Вполне представляется. на все что больше 3 вольт digitalRead будет возвращать 1, на "меньше одного вольта" - вернет 0.

Можно перевесить на цифровой пин, а можно digitalRead делать прямо с аналогово пина. Вам же не нужны "промежуточные состояния", поэтому его вполне можно "волевым решением" переименовать в "цифровой" :)

Трудности могут возникнуть только если при поднесении магнита, он будет выдавать что-то меньше чем 3 вольта.

leshak
Offline
Зарегистрирован: 29.09.2011

Попробуйте что-то типа: 

void loop(){
  Serial.print("A=");Serial.print(analogRead(0),DEC));  // читаем как аналоговый
  Serial.print(";  D=");Serial.print(digitalRead(A0),DEC)); // читаем как цифровой
  Serial.println();
  delay(300);
}

И помашите магнитом перед датчиком, посмотрете что в Serial бежать будет.

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

Попробуйте что-то типа: 

void loop(){
  Serial.print("A=");Serial.print(analogRead(0),DEC));  // читаем как аналоговый
  Serial.print(";  D=");Serial.print(digitalRead(A0),DEC)); // читаем как цифровой
  Serial.println();
  delay(300);
}

И помашите магнитом перед датчиком, посмотрете что в Serial бежать будет.

В коде использую конструкцию if, значение 491 это нейтральное положение, т.е. без магнита.

int pins[] = {2,3,4,5,6,7,8,9,10,11,12,13};	// an array of pin numbers
int col_len = 12;		// column length

// customizable parameters
int timer1 = 4;			// time between columns
int timer2 = 20;		// time between frames
int timer3 = 10;			// time between drawings
int frame_len = 22;		// frame length
int frame_num = 1;		// number of frames
int sensorPin0 = A0;
// data corresponding to the image to be displayed
int data[] = {0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,0 ,0 ,0 ,0 ,0}; 

void setup()
{
  pinMode(sensorPin0, INPUT);
	int i;
	for (i = 0; i < col_len; i++)
		pinMode(pins[i], OUTPUT);	// set each pin as an output
}

void loop()
{
if (analogRead(sensorPin0)<491)	
   {int a,b,c;
	
	// go through all data for all columns in each frame.
	for (a = 0; a < frame_num; a++)
	{
		for (b = 0; b < frame_len; b++)
		{
			for (c = 0; c < col_len; c++)
			{
				if (data[a*frame_len*col_len + b*col_len + c] == 0) {digitalWrite(pins[c], LOW);}
				else {digitalWrite(pins[c], HIGH);}
			}
			delay(timer1);
		}
		for (c = 0; c < col_len; c++)
		{digitalWrite(pins[c], LOW);}
		delay(timer2);}
	
	delay(timer3);
{int a,b,c;
	
	// go through all data for all columns in each frame.
	for (a = 0; a < frame_num; a++)
	{
		for (b = 0; b < frame_len; b++)
		{
			for (c = 0; c < col_len; c++)
			{
				if (data[a*frame_len*col_len + b*col_len + c] == 0) {digitalWrite(pins[c], LOW);}
				else {digitalWrite(pins[c], HIGH);}
			}
			delay(timer1);
		}
		for (c = 0; c < col_len; c++)
		{digitalWrite(pins[c], LOW);}
		delay(timer2);}
}
}}

 

 

Хорошо, сейчас попробую залить.

maksim
Offline
Зарегистрирован: 12.02.2012

 То есть 491 - без магнита, а с магнитом еще ниже падает?

renat56
Offline
Зарегистрирован: 04.04.2012

maksim пишет:

 То есть 491 - без магнита, а с магнитом еще ниже падает?

Смотря каким полюсом поднести, если северным - до 200 падает, а если южном то около 600 получается. Я прикрепил магнит северным полюсом, почему то чувствительность лучше оказалась.

leshak
Offline
Зарегистрирован: 29.09.2011

 А в вашем скетче попробуйте, в строке 24, вместо analogRead(sensorPin0), сделать analogRead(0). Потому что есть сильно подозрение что вы пытаетесь читать несуществующий пин.

Каждый аналоговый пин имеет как-бы "два номера". "Аналоговый номер" и "цифровой номер".

analogRead ждет именно "аналоговый номер", ему нужно давать "просто цифру" - 0. А digitalRead-ду нужно давать "цифровой номер". Он имеет "псевдоним" A0.

А когда вы "смешиваете" и делате analogRead(A0) - вы фактически пытаетесь, на самом деле прочитать analogRead(14). То есть даете команду на чтение 14-того аналогового пина, которого - не существует.

P.S. И на input не обязательно включить порты. Они по умолчанию уже INPUT

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

 А в вашем скетче попробуйте, в строке 24, вместо analogRead(sensorPin0), сделать analogRead(0). Потому что есть сильно подозрение что вы пытаетесь читать несуществующий пин.

Каждый аналоговый пин имеет как-бы "два номера". "Аналоговый номер" и "цифровой номер".

analogRead ждет именно "аналоговый номер", ему нужно давать "просто цифру" - 0. А digitalRead-ду нужно давать "цифровой номер". Он имеет "псевдоним" A0.

А когда вы "смешиваете" и делате analogRead(A0) - вы фактически пытаетесь, на самом деле прочитать analogRead(14). То есть даете команду на чтение 14-того аналогового пина, которого - не существует.

P.S. И на input не обязательно включить порты. Они по умолчанию уже INPUT

Хорошо, поправлю. В принципе этот скетч работает, все включается в нужый момент. Просто хочу дополнить его что бы мог следить за интервалами времени между срабатыванием датчика.

 

leshak
Offline
Зарегистрирован: 29.09.2011

 >То есть даете команду на чтение 14-того аналогового пина, которого - не существует.

Посмотрел в реализацию analogRead() - похоже там предусмотрели защиту "от подобного случая" и корректируют номер пина внутри. Так что, похоже, можно оставить "как есть", но я бы поменял. Хотя-бы для того что-бы совпадать с документацией на эту функцию.

leshak
Offline
Зарегистрирован: 29.09.2011

 > Просто хочу дополнить его что бы мог следить за интервалами времени между срабатыванием датчика.

prevRaiseTime=raiseTime; // запомнили "прошлое время сработки"
raiseTime=millis(); // запомнили текущие

interval=raiseTime-prevRaiseTime;// а вот и время между сработаки


Только что-бы это "работало как нужно" потребуется отказаться от использования delay() внутри главного цикла loop().

Везеде где нужны "долгие действия" пользоватся только функцией millis() и вычислять "сколько прошло времени". Посмотрите пример, в разделе "Программирования" - "Мигаем без использования delay()" 

Но точнее и феншуйней, конечно, добится, все-таки его сработки "как цифрового". Тогда можно будет воспользоваться функцией attachInterrupt и резко снизить зависимость от таймингов главного loop()

maksim
Offline
Зарегистрирован: 12.02.2012

Ну тогда заведите переменную (советую для этой переменной тип unsigned long, например millis_old) в которой будете хранить предыдущее значение millis() и переменную (например circle), в которую будет сохраняться разница между текущим значением millis() и предыдущем. И в момент срабатывания, у вас это 26 строка. 

circle = millis() - millis_old;

 

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

 > Просто хочу дополнить его что бы мог следить за интервалами времени между срабатыванием датчика.

prevRaiseTime=raiseTime; // запомнили "прошлое время сработки"
raiseTime=millis(); // запомнили текущие

interval=raiseTime-prevRaiseTime;// а вот и время между сработаки


Только что-бы это "работало как нужно" потребуется отказаться от использования delay() внутри главного цикла loop().

Везеде где нужны "долгие действия" пользоватся только функцией millis() и вычислять "сколько прошло времени". Посмотрите пример, в разделе "Программирования" - "Мигаем без использования delay()" 

Но точнее и феншуйней, конечно, добится, все-таки его сработки "как цифрового". Тогда можно будет воспользоваться функцией attachInterrupt и резко снизить зависимость от таймингов главного loop()

arduino.cc/playground/Learning/Tachometer

Здесь организовывают работу тахометра, я так понимааю там при определенном значении аналогового сигнала вход либо LAW, либо HIGH

val=analogRead(0);
if(val<sens)
stat=LOW;
else
stat=HIGH;

Возможно так сделать?

maksim
Offline
Зарегистрирован: 12.02.2012

 А по поводу задержек я думаю можно не волноваться, так как эти все задержки заканчиваются раньше одного оборота колеса.

leshak
Offline
Зарегистрирован: 29.09.2011

 

renat56 пишет:

arduino.cc/playground/Learning/Tachometer

Здесь организовывают работу тахометра, я так понимааю там при определенном значении аналогового сигнала вход либо LAW, либо HIGH

val=analogRead(0);
if(val<sens)
stat=LOW;
else
stat=HIGH;

Возможно так сделать?

Вы уже сами ответили на этот вопрос. Принципиально это ничем не отличается от вашего скетча. Просто ввели дополнительноую промежуточную переменную. Принцип - абсолютно идентичен с вашим кодом.

renat56
Offline
Зарегистрирован: 04.04.2012

 

leshak пишет:

Попробуйте что-то типа: 

void loop(){
  Serial.print("A=");Serial.print(analogRead(0),DEC));  // читаем как аналоговый
  Serial.print(";  D=");Serial.print(digitalRead(A0),DEC)); // читаем как цифровой
  Serial.println();
  delay(300);
}

И помашите магнитом перед датчиком, посмотрете что в Serial бежать будет.

Попробывал, выдает вне зависимости есть или нет магнита, это:

A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
 

...................

 

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

 

renat56 пишет:

arduino.cc/playground/Learning/Tachometer

Здесь организовывают работу тахометра, я так понимааю там при определенном значении аналогового сигнала вход либо LAW, либо HIGH

val=analogRead(0);
if(val<sens)
stat=LOW;
else
stat=HIGH;

Возможно так сделать?

Вы уже сами ответили на этот вопрос. Принципиально это ничем не отличается от вашего скетча. Просто ввели дополнительноую промежуточную переменную. Принцип - абсолютно идентичен с вашим кодом.

Спасибо. Попробую подстроить под свой скетч. 

maksim
Offline
Зарегистрирован: 12.02.2012

renat56 пишет:

 

leshak пишет:

Попробуйте что-то типа: 

void loop(){
  Serial.print("A=");Serial.print(analogRead(0),DEC));  // читаем как аналоговый
  Serial.print(";  D=");Serial.print(digitalRead(A0),DEC)); // читаем как цифровой
  Serial.println();
  delay(300);
}

И помашите магнитом перед датчиком, посмотрете что в Serial бежать будет.

Попробывал, выдает вне зависимости есть или нет магнита, это:

A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
A=10; D=10
 

...................

 

Не может такого быть. вот у себя проверил все работает.

void setup() {
  Serial.begin(9600);
}

void loop(){
  Serial.print("A=");
  Serial.print(analogRead(0),DEC);  // читаем как аналоговый
  Serial.print(";  D=");
  Serial.print(digitalRead(A0),DEC); // читаем как цифровой
  Serial.println();
  delay(300);
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

 А по поводу задержек я думаю можно не волноваться, так как эти все задержки заканчиваются раньше одного оборота колеса.

А если человек даунхилом решит занятся? ;)  (ночью в темноте ;)  ) Или любитель кататся за фурами в аэродинамическом мешке :)

Вообщем наверное вы правы, просто меня всегда смущают delay() в чем-то более сложном чем "проверочный примерчик на 5-ть строк".

maksim
Offline
Зарегистрирован: 12.02.2012

leshak пишет:

maksim пишет:

 А по поводу задержек я думаю можно не волноваться, так как эти все задержки заканчиваются раньше одного оборота колеса.

А если человек даунхилом решит занятся? ;)  (ночью в темноте ;)  ) Или любитель кататся за фурами в аэродинамическом мешке :)

Вообщем наверное вы правы, просто меня всегда смущают delay() в чем-то более сложном чем "проверочный примерчик на 5-ть строк".

Будут глюки при больших ускорениях, при увеличении скорости может пропустить срабатывание датчика, а при уменьшении надпись сожмется.

leshak
Offline
Зарегистрирован: 29.09.2011

 >Попробывал, выдает вне зависимости есть или нет магнита,

Покажите весь скетч который вы заливали...(или возмите версию Максима) вы копировали мой код или руками набирали? Ну не может там быть D=10, так как digitalRead ничего больше 1 возвращать не умеет.

renat56
Offline
Зарегистрирован: 04.04.2012

leshak пишет:

 >Попробывал, выдает вне зависимости есть или нет магнита,

Покажите весь скетч который вы заливали...(или возмите версию Максима) вы копировали мой код или руками набирали? Ну не может там быть D=10, так как digitalRead ничего больше 1 возвращать не умеет.

Сорри, все работает. Скобку лишнюю поставил.

maksim
Offline
Зарегистрирован: 12.02.2012

А как работает? Какие показания?

renat56
Offline
Зарегистрирован: 04.04.2012

maksim пишет:

А как работает? Какие показания?

В общем вот, поднес магнит северным полюсом:


A=500;  D=1
A=499;  D=1
A=499;  D=1
A=506;  D=1
A=487;  D=1
A=481;  D=1
A=445;  D=0
A=452;  D=0
A=457;  D=0
A=471;  D=0
A=477;  D=0
A=469;  D=0

 

maksim
Offline
Зарегистрирован: 12.02.2012

Ну вот видите как хорошо, ваш датчик работает как цифровой. А значит вы можете воспользоваться аппартным прерыванием для вычисления времени круга и для вычисления задержек свечения светодиодов.

maksim
Offline
Зарегистрирован: 12.02.2012

Проверьте как будет работать ваш код если заменить 24 строку вот на эту:

if (!digitalRead(sensorPin0)) 

 

renat56
Offline
Зарегистрирован: 04.04.2012

Ну вот и разобрались, а можно подробнее про функцию attachInterrupt. По феншую так по феншую) 

maksim
Offline
Зарегистрирован: 12.02.2012

Только для этого вам нужно будет подключить датчик к 2 или 3 цифровому выводу.

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

Ну вот видите как хорошо, ваш датчик работает как цифровой. А значит вы можете воспользоваться аппартным прерыванием для вычисления времени круга и для вычисления задержек свечения светодиодов.

это значит сделать attachIntterupt и перевесится на 2 или 3-тий цифровой пин. Вообщем прочитать про функцию attachInterrupt (но в налачале имеет смысл проверить то что предложил Максим).

Если датчик будет сильно "шуметь" (на границе, при подносе/удалении выдвать несколько 0,1,0,1,0,1 пока не устоновится в что-то стабильное), то можно просто игнорировать интервалы меньше какого-то, заведомо слишком малого значения.

renat56
Offline
Зарегистрирован: 04.04.2012

maksim пишет:

Проверьте как будет работать ваш код если заменить 24 строку вот на эту:

if (!digitalRead(sensorPin0)) 

 

Попробывал, работает. Но слишком малая чувствительность магнит нужно вплотную подносить.

maksim
Offline
Зарегистрирован: 12.02.2012

Примерно так

#define RPMpin 2 // датчик Холла
unsigned long microsold = 0;
volatile unsigned int circle = 0;

void setup() { 
  Serial.begin(9600);
  attachInterrupt(0, RPM, FALLING);
}

void loop() {
  Serial.println(circle, DEC); // об/мин
}

void RPM (){
  circle = micros() - microsold;
  microsold = micros();
}

это если ко 2 выводу, а если к 3 то в 7 строке надо поменять 0 на 1.

leshak
Offline
Зарегистрирован: 29.09.2011

 >Но слишком малая чувствительность магнит нужно вплотную подносить.

Варианты:

  • Искать другой датчик
  • Взять другой магнит, ниодимовый какой-нибудь, вот тут  например из старого CD-рома выковыривали
  • Какой-нибудь простенький усилитель на транзисторе присобачить (тут, я думаю, Максим лучше подсказать сможет).
renat56
Offline
Зарегистрирован: 04.04.2012

maksim пишет:

Примерно так

#define RPMpin 2 // датчик Холла
unsigned long microsold = 0;
volatile unsigned int circle = 0;

void setup() { 
  Serial.begin(9600);
  attachInterrupt(0, RPM, FALLING);
}

void loop() {
  Serial.println(circle, DEC); // об/мин
}

void RPM (){
  circle = micros() - microsold;
  microsold = micros();
}

это если ко 2 выводу, а если к 3 то в 7 строке надо поменять 0 на 1.

хорошо, завтра попробую

maksim
Offline
Зарегистрирован: 12.02.2012

И теперь вместо например delay(timer1); делаете например delay(circle/50); и тогда при прохождении круга раньше чем отрисовалась надпись задержка будет меняться сразу по середине прорисовки.
 

maksim
Offline
Зарегистрирован: 12.02.2012

leshak пишет:

 >Но слишком малая чувствительность магнит нужно вплотную подносить.

Варианты:

  • Искать другой датчик
  • Взять другой магнит, ниодимовый какой-нибудь, вот тут  например из старого CD-рома выковыривали
  • Какой-нибудь простенький усилитель на транзисторе присобачить (тут, я думаю, Максим лучше подсказать сможет).

Для этих целей идиально подходит операционный усилитель с подстроечным резистором, но это уже лишнее.... 

Можно дополнительно включить подтяжку и поставить ниодимовый магнит, кстати они есть в винтах (HDD), только форма у них в виде полусогнутой сордельки.