Нужна помощь в написании программы
- Войдите на сайт для отправки комментариев
Чт, 24/05/2012 - 16:10
Друзья, накидайте простую програмку, плиз. В благодарность пошлю лучиков добра и кину 500руб на телефон.
Есть сервопривод продолжительного вращения, ардуина и кнопка. Надо, чтобы при старте привод начинал крутиться пока не нажмется кнопка, после нажатия задержка 30сек и опять крутиться в туже сторону. И так покругу. Грубо говоря нажатие кнопки должно останавливать серво на полминуты
Привод на 9 выходе, а кнопка на аналоговом входе 0
А у сервы нулевое положение когда она не крутится какое? myservo.write(???) И у вас точно серва, а не мотор-редуктор?
Хм... не совсем я ясно что за "сервопривод продолжительного вращения", у него есть имя? Какая-то документация?
Если срочно - можете контакты, свои, какие-то оставить, может в скайпе это быстрее решится.
Черновик будет выглядить примерно так (в предопожении что для вращения на 9-ты пин нужно просто 5v, а кнопка подключена к земле (хотя зачем ее садить на аналоговый?).
#define BUTTON_PIN A0 // пин кнопки #define MOTOR_PIN 9 #define PAUSE_DELAY 30000 // время паузы в миллисекундах unsigned long waitTime=0; void setup(){ pinMode(MOTOR_PIN,OUTPUT); // мотор на выход pinMode(BUTTON_PIN,INPUT); // кнопку на вход digitalWrite(BUTTON_PIN,HIGH);// подключили подтягивающий } void loop(){ if(waitTime<millis()){ //уже пора крутить? digitalWrite(MOTOR_PIN,HIGH); //крутим мотор if(!digitalRead(BUTTON_PIN)){ //кнопка нажата? waitTime=millis()+PAUSE_DELAY; // вычисляем когда опять крутить можно будет } } else { // еще не пришло время крутить, waitTime>millist() digitalWrite(MOTOR_PIN,LOW); //отстанавливаем мотор } }Кнопка при нажатии должна замыкать 4 пин на землю (GND).
#include <Servo.h> #define BUTTON 4 #define SERVO 9 #define DELAY 30 // Время задержки в СЕКУНДАХ #define STOP 90 #define MOVE 180 Servo servo; boolean btn_stt = 0; void setup() { digitalWrite(BUTTON, 1); servo.attach(SERVO); // attaches the servo on pin 9 to the servo object } void loop() { servo.write(MOVE); // Крутим серву if(!digitalRead(BUTTON) && !btn_stt){ // Если кнопка нажата то servo.write(STOP); // Останавливаем серву btn_stt = 1; delay(DELAY*1000); // Ждем } delay(200); if(digitalRead(BUTTON)){ btn_stt = 0; } }Сервомотор такой http://www.electronshik.ru/card/servomotor-analogoviy-87-kg8729sm-sm-s4309r-360176-112475
В течении часа отпишусь как работает код
Выдает
sketch_may24b.cpp: In function 'void loop()':
sketch_may24b:21: error: 'myservo' was not declared in this scope
Перекопируйте еще раз, я поправил.
Ну раз это, все-таки серва, то мой код будет примерно проапдейтится до такого:
#include <Servo.h> #define BUTTON_PIN A0 // пин кнопки #define SERVO_PIN 9 #define PAUSE_DELAY 30000 // время паузы в миллисекундах #define ROTATE 180 // угол при котором серва крутит по часовой, можно попрбовать увеличить, если нужно "быстрее крутить" #define STOP 90 // угол при котором серва стоит, возможно чуток опытным путем подобрать нужно будет Servo servo; unsigned long waitTime=0; void setup(){ servo.attach(SERVO_PIN); // подключаемся к серве pinMode(BUTTON_PIN,INPUT); // кнопку на вход digitalWrite(BUTTON_PIN,HIGH);// подключили подтягивающий } void loop(){ if(waitTime<millis()){ //уже пора крутить? servo.write(ROTATE); //крутим серву if(!digitalRead(BUTTON_PIN)) waitTime=millis()+PAUSE_DELAY; // если кнопка нажата вычисляем когда опять крутить можно будет } else servo.write(STOP); // еще не пришло время крутить, waitTime>millist(),стопорим серву }Возможно нужно будет поигратся, подобрать константы ROTATE и STOP (условные углы при которых серва крутит и стоит, они могут отличатся от экземпляра к эксземпляру).
P.S. Кнопка на нулевом аналоговом пине, как вы описали в стартовом посте, подключена от пина к земле.
Максим вы уже второй раз меня выручаете. Телефон у вас не менялся?
Скиньте мне свою почту или скайп, чтоб я в следующий раз напрямую
а я бы так сделал:
Серво на пин 9;
кнопка должна замыкать GND и RESET
#include <Servo.h> Servo myservo; void setup() { delay(30000); myservo.attach(9); } void loop() { myservo.write(20); }А если это не просто лежащие на столе серва и кнопка, которую кто-то нажимает, а некое устройство у которого серва что-то вращает, а кнопка выступает например в качестве концевого выключателя, т.е. серва делает один оборот и нажимает на кнопку, кнопка остается нажатой пока серва не начнет снова крутиться.
Да я смеюсь :)
Да я смеюсь :)
В смысле?
В смысле мой sketch - это шутка:)
Если просто кнопка и серва то нормальный скейтч, только можно myservo.write(20); из лупа в сетап перенести.
Добрый день.Maksim вы не поможете написать небольшой скетч по техзаданию?
Помогу.
Максим,скайп включите пожалуйста.
Я сейчас на работе, дома буду примерно через час, вы пока в скайпе в контакты добавьтесь и техзадание подготовьте.
Доброе время суток. По работе возникла необходимость осуществлять контроль изменения погодных условий. А именно скорости и направления ветра, ну и температуры (как дополнение).
Для осуществления контроля температуры - датчик ds18b20, скорость ветра будет измеряться путем счета количества импульсов в единицу времени (для этого планируется использовать энкодер от шариковой мышки, подключенный к чашечному анемометру), а направление ветра будет определяться путем считывания трехзначного бинарного кода с оптопар, расположенных под флюгером, к которому будет зактреплен диск с прорезями, разделенный на 8 секторов (в соответствии с количеством сторон света и промежуточными - С, С-З, З, Ю-З, Ю, Ю-В, В, С-В). В качестве устройства отображения информации будет использован LCD дисплей 16*2. Ну и конечно часы реального времени на микросхеме DS1307. Программку накатал, используя уже имеющиеся проекты (за неё прошу сильно не бить - знаю, что много проблеммных вопросов, поэтому и выкладываю, чтоб помогли, если что не так). Вот, собственно она родимая и есть:
#include <MyLiquidCrystalRus.h> #include <TimerOne.h> #include <DallasTemperature.h> #include <WProgram.h> #include <Wire.h> #include <DS1307.h> MyLiquidCrystalRus lcd(12, 11, 5, 4, 3, 2); DallasTemperature tempSensor; int rtc[7]; byte gradus[8] = { B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000, }; // создаем собственный символ "градус Цельсия" void setup() { RTC.stop(); //установка времени RTC.set(DS1307_SEC,0); RTC.set(DS1307_MIN,26); RTC.set(DS1307_HR,15); RTC.set(DS1307_DOW,6); RTC.set(DS1307_DATE,15); RTC.set(DS1307_MTH,06); RTC.set(DS1307_YR,12); RTC.start(); pinMode(9, INPUT); Timer1.initialize(); // инициализация таймера - 1 секунда по умолчанию Timer1.attachInterrupt( timerIsr ); lcd.begin(16, 2);// указываем сколько строк и символов дисплея lcd.createChar(1, gradus); // присваиваем символу "градус Целься" порядковый номер lcd.print("DS18B20 тест"); tempSensor.begin(13); // запуск датчика DS18B20 lcd.setCursor(12, 0); lcd.print("норма"); delay(1000); lcd.clear(); } void loop() { //---------------------------- Обработка времени--------------------------- RTC.get(rtc,true); // первая строка: время, дата, lcd.setCursor(0,0); //вывод на дисплей времени printDigits(rtc[2],':'); printDigits(rtc[1],' '); printDigits(rtc[4],'/'); //вывод на дисплей даты printDigits(rtc[5],'/'); printDigits(rtc[6],' '); //---------------------------- Обработка температуры------------------------ lcd.setCursor(12,1); int Temp=(int(tempSensor.getTemperature()*10))/10; //выводим информацию температуры на дисплей, одновременно избавляясь от сотых if (Temp>0) { lcd.print("+");} else {lcd.print("-");} lcd.print(Temp); lcd.print("\1C"); //символ "градус Цельсия" delay(100); //---------------------------- Обработка направления ветра--------------- int p1[2]; lcd.setCursor(0,1); for(int i=0; i<2; i++) { p1[i] = digitalRead(i +6); } // опрашиваем i –ый+6 вывод if (p1[]== {1, 1, 1}){ lcd.print ("С"); } //Выдает ошибку, такая работа с массивом не допустима. Как быть? if (p1[]== {1, 1 ,0}) { lcd.print ("С-З"); } if (p1[]== {1, 0, 0}) { lcd.print ("З"); } if (p1[]== {1, 0, 1}) { lcd.print ("Ю-З"); } if (p1[]== {0, 0, 1}) { lcd.print ("Ю"); } if (p1[]== {0, 0, 0}) { lcd.print ("Ю-В"); } if (p1[]== {0, 1, 0}) { lcd.print ("В"); } if (p1[]== {0, 1, 1}) { lcd.print ("С-В"); } } void printDigits(int digits, char delimiter) { if(digits < 10) { lcd.print('0'); } lcd.print(digits,DEC); lcd.print(delimiter); } //---------------------------- Обработка скорости ветра--------------------------- void timerIsr() {int v=0; if (digitalRead(9) == HIGH) // условие на опрос 9го порта { int v=v+1; } else { int v=v+0; } lcd.setCursor(5,1); lcd.print(v, ' м/с' ); //в зависимости от анемометра придется калибровать if(v > 25) // Вывод сообщения о превышении заданного значения скорости ветра { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Внимание!!!"); // Предупреждение при увеличении ветра delay(2000); lcd.clear(); }}Вот собственно и все. За замечания и исправления (в частности по работе с массивом при определении направления ветра) буду очень признателен.
Можно считать не количество импульсов за единицу времени, а время между импульсами. Тогда информация будет более оперативной и точной, а если оперативность не нужна, то можно упростить и сделать более надежной конструкцию тахометра - использовав датчик Холла (взятый, например, из куллера от компа).
И как то вы не в тему здесь об этом написали, можно было бы создать новую...
Ну как бы тут люди с вопросом помощи написания программы обращаются, вот я и подумал, что можно сюда свои вопросы воткнуть... Хотя согласен, можно было и в отдельную тему закинуть...
По поводу использования двигателя от компа в качестве измерителя скорости ветра читал довольно много статеек в интернете. Там говорят, что зависимость напряжения на выходе от скорости вращения нелинейная, а значит падает точность измерения.
Ну а на счет времени между импульсами - идея интересная, хотя я пока плохо себе представляю как это должно выглядеть в программе. И, честно говоря, хотелось бы обойтись без #include <TimerOne.h>...
Подскажите, пожалуйста, как можно решить возникшую в моей программе проблему с массивами при определении направления ветра. Весь мозг себе сломал...
Спасибо.
Использовать нужно не сам куллер (хотя можно и сам) а датчик Холла, который в нем установлен и нужно мерить не напряжерие, а сигнал с этого датчика, который дает импульс за один оборот куллера - это более простой и более надежный способ, чем ИК-энкодер от мышки. Т.е. если не хотите (или не нужно) использовать сам куллер, то разбераете его, достаете оттуда датчик Холла (или покупаете датчик Холла отдельно), затем на вращающуюся часть анемометра устанавливаете магнит, который при вращении будет проходить рядом с датчиком. Вот темка, там есть пример кода
А по поводу определения направления не совсем понятно что у вас там такое.... каким образом оно определяется?
p1 у вас объявлен как массив из двух элементов, и читаете вы тоже два элемента. А пытаетесь сравнить с массиво из трех элементов.
Так что вопрос, даже, не в том что вы сравниваете массивы неправильно, а в том что сравниваете нестравнимое. Вернее "сравнимое", но совпадений у вас не будет никогда. Два байта никогда не будут равны трем байтам :)
И еще, массивы-то, конечно можно сравнить, только массивы вам тут не нужны (и уж тем более массив int-ов). У вас же digitalRead возвращает 0 и 1, то есть биты. И нужно вам их 3 штуки. Для их хранения с лихвой хватит одного байта (в него восемь бит влезть может). И память экономите (в шесть раз) и сравнивать можно обычными операторами сравнения.
byte p1=0; for(byte i=0;i<3;i++)p1=p1 | ( digitalRead(i+6)<< (2-i) ); // читаем 6,7,8-мую ноги и закидываем их в три младших бита. switch(p1){ case B111: lcd.print ("С");break; case B110: lcd.print ("С-З");break; case B100: lcd.print ("З");break; case B101: lcd.print ("Ю-З");break; case B001: lcd.print ("Ю");break; case B000: lcd.print ("Ю-В");break; case B010: lcd.print ("В");break; case B011: lcd.print ("С-В");break; default: lcd.print("Это фигня какая-то, а не ежик"); // почему-то не совпало ни с одной кобинацией }Немного перемудрил с чтением ног. Хотя будет работать, но можно чуток попроще
Ё-моё, а я и не задумался, что он всего два значения считывает... leshak, огромное спасибо, что помогли разобраться с этим вопросом! Шутка с ёжиком понравилась - включу в программу эту строку! :-)
maksim, прочитал про датчик Холла и не могу понять почему это будет надежнее... Разница с оптопарой лишь в физике процесса получения сигнала на датчик. Механику придется по-другому продумывать... А так один в один...
Очень благодарен вам за то, что не оставили мою проблему без внимания.
maksim, ссылочку, которую вы дали - там осуществляется счет времени между импульсами и высчитывается количество оборотов за минуту? Для проверки подключал кнопку. Выдает просто нереальные значения... Бывали и отрицательные. Скорее всего я что-то неправильно понимаю...
Из-за дребезга вашей кнопки получаются не реально короткие промежутки между импульсами, поэтому вы и получаете такие огромные значения, а так как переменная rpm имеет тип int, то ее максимальное значение 32767 (чего более чем достаточно для об/мин) отсюда при переполнении этой переменной у вас получаются отрицательные числа.
#define RPMpin 2 // датчик Холла unsigned long microsold = 0; volatile int rpm = 0; void setup() { Serial.begin(9600); digitalWrite(RPMpin, 1); //attachInterrupt(0, RPM, RISING); attachInterrupt(0, RPM, FALLING); } void loop() { Serial.println(rpm, DEC); // об/мин } void RPM (){ rpm = (1000000.0/(micros() - microsold))*60; microsold = micros(); }Проверил на кнопке с параллельно включенным ей конденсатором - все замечательно работает.
Так как анемометр стоит на открытой местности, то прийдется защищать ИК датчик от солнца и от пыли. И не нужно думать как закрепить колесо от мышки на анемометре, магнит закрепить гараздо проще.
И то верно... Хотя у меня в планах было сделать корпус, ведь там еще и ИК датчики от флюгера будут...
А в этих строках:
в зависимости от того какую строку закомментировать будет зависеть какой уровень (лог. 1 или лог. 0) будет ожидать на pin2?
Почти. Эти строки указывают при каком событии должна быть вызванна функция RPM (обработчик прерывания)
По сути, при отключенных датчиках скорости и направления ветра он должен показывать Ю-В 0 м/с, но по факту скорость ветра скачет и показывает три значения: -10, -11, -91... Хотя просто вставил эти строки в программу.
#include <MyLiquidCrystalRus.h> #include <DallasTemperature.h> #include <WProgram.h> #include <Wire.h> #include <DS1307.h> #define RPMpin 9 // ИК датчик unsigned long microsold = 0; volatile int rpm = 0; MyLiquidCrystalRus lcd(12, 11, 5, 4, 3, 2); DallasTemperature tempSensor; int rtc[7]; byte gradus[8] = { B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000, }; // создаем собственный символ "градус Цельсия" void setup() { /*RTC.stop(); //установка времени RTC.set(DS1307_SEC,0); RTC.set(DS1307_MIN,26); RTC.set(DS1307_HR,15); RTC.set(DS1307_DOW,6); RTC.set(DS1307_DATE,15); RTC.set(DS1307_MTH,06); RTC.set(DS1307_YR,12); RTC.start(); */ lcd.begin(16, 2); // указываем сколько строк и символов дисплея lcd.createChar(1, gradus); // присваиваем символу "градус Целься" порядковый номер tempSensor.begin(13); // запуск датчика DS18B20 digitalWrite(RPMpin, 1); attachInterrupt(0, RPM, FALLING); } void loop() { //---------------------------- Обработка времени--------------------------- RTC.get(rtc,true); // первая строка: время, дата, день недели lcd.setCursor(0,0); //вывод на дисплей времени printDigits(rtc[2],':'); printDigits(rtc[1],' '); printDigits(rtc[4],'/'); //вывод на дисплей даты printDigits(rtc[5],'/'); printDigits(rtc[6],' '); //---------------------------- Обработка температуры--------------------------- lcd.setCursor(11,1); int Temp=(int(tempSensor.getTemperature()*10))/10; //выводим информацию температуры на дисплей, одновременно избавляясь от сотых if (Temp>0) { lcd.print("+");} else {lcd.print("-");} lcd.print(Temp); lcd.print("\1C"); //символ "градус Цельсия" delay(100); //---------------------------- Обработка направления ветра --------------------------- byte p1=0; lcd.setCursor(0,1); for(byte i=0;i<3;i++)p1= (p1 <<1) | digitalRead(i+6); // читаем 6,7,8-мую ноги и закидываем их в три младших бита. switch(p1){ case B111: lcd.print ("С ");break; case B110: lcd.print ("С-З");break; case B100: lcd.print ("З ");break; case B101: lcd.print ("Ю-З");break; case B001: lcd.print ("Ю ");break; case B000: lcd.print ("Ю-В");break; case B010: lcd.print ("В ");break; case B011: lcd.print ("С-В");break; } //---------------------------- Обработка скорости ветра--------------------------- lcd.setCursor(4,1); lcd.print(rpm, DEC); // об/мин lcd.setCursor(7,1); lcd.print("м/с"); if(rpm > 25) // Вывод сообщения о превышении скорости ветра { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Внимание! "); // Предупреждение при увеличении ветра } } //---------------------------- Обработка скорости ветра--------------------------- void RPM (){ rpm = (1000000.0/(micros() - microsold))*60; microsold = micros(); } //---------------------------- Обработка показания времени--------------------------- void printDigits(int digits, char delimiter) { if(digits < 10) { lcd.print('0'); } lcd.print(digits,DEC); lcd.print(delimiter); }Где-то я накосячил... :-(
Вот как это выглядит на деле
>скорость ветра скачет
А зачем вам так часто обновлять показания ветра?
Просто заведите счетчик, увеличивайте его в обработчике прерывания.
А в loop, раз в секунду выводите его умноженный на 60-т (для перевода в минуту). Вот у вас и получится "усредненное за секунду". Уже не будет так прыгать. Если датчик оборотов дает дребезг, можно в этом обработчике еще запоминать время, когда был последний импульс, если прошло, например, менше 200 миллесекунд - игнорить, это дребезг.
За совет, конечно, огромное спасибо, но для моего уровня знания Ардуины - это крутовато... Ладно, будем эксперементировать...
А нужно было не боятся, а попробовать его реализовать. Голова боится, а руки делают. Тем более что в свое изначальном скетче, хоть и кривовато, но именно по этому пути вы вначале и пошли.
Счас попробую накидать. Я правильно понял, что у вас один тик RMP-ма означает метр в секунду?
Вообщем примерно так:
unsigned long next_rmp_report_time=0; volatile unsigned long rmp=0; volatile unsigned long last_rmp_time; // время когда последний раз сработал rmp-датчик #define RMP_DEBOUNCE 200 // если RMP будет выдать тики чаще чем это время, в миллисекундах, он будет игнорироватся. защита от дребезга. подбираете опытным путем. слишком мало - начнет ловить дребезг. слишком много - начнет пропускать реальные тики. #define RMP_TIKS_IN_M_PER_SEC 4 // предположим что при скорости 1 м/c датчик тикает 4-ре раза за секунду. Чем выше это значение - тем "плавнее" будет показания. void setup(){ attachInterrupt(0, RPM, FALLING); } void loop(){ if(next_rmp_report_time<millis()){ // пришло время вывести скорость lcd.setCursor(4,1); lcd.print( (rpm/RMP_TIKS_IN_M_PER_SEC)*60 , DEC); // об/мин, умонжаем на 60-т, так как счетчик накопил данные за секунду, а репортим в минутах lcd.setCursor(7,1); lcd.print("м/с"); next_rmp_report_time=millis()+1000;// вычисляем время когда будет нужне следующий отчет, через одну секунду rmp=0; // обнуляем счетчик, пошли заново считать } } void RPM (){ if(millis()-last_rmp_time>RMP_DEBOUNCE){ // прошло доcтаточно времени с предыдущего тика, это не дребезг rmp++; // банально увеличили счетчик } last_rmp_time=millis();// запомнили время когда последний раз тикнул RPM }Если на маленьких скоростях датчик не успевает тикнуть вообще ни разу (или слишком редко), можно репортить не раз в секунду, а раз в 10-ть сек
Вот это круто! После такого подробного описания и помощи желание изучать это устройство возрастает в разы! Даже не знаю как и благодарить...
Как только дождусь посылку с заказанными радиоэлементами, полностью соберу механику и выложу готовое устройство.
Уважаемые знатоки, у меня возник резонный вопрос следующего содержания - какую длину проводника осилит Ардуино? Другими словами на какую длину провода я смогу вытянуть свои датчики?
http://bit.ly/KRORwa
Изучил, спасибо.
А есть ли возможность при работе с микросхемой DS1307 выводить на дисплей год не полностью, а лишь последние две цифры?
Действующие лица:
Гопник: эй, мужик!!! какой сейчас год?
Прохожий: (смотрит на часы), сейчас....(начинает вывод на дисплей)
-----
Вопрос: сможет ли прохожий сказать гопнику только последние цифры года? Как связанна полнота ответа прохожего с моделью часов?
:-D Очень доступно и с юмором!!! :-D Спасибо за разъяснение!
По последнему вопросу нашел решение. В кусочке программы
достаточно изменить строку 62 следующим образом:
и выводиться на дисплей будет не 17/06/2012, а 17/06/12