#include <Bounce.h>
#include <Servo.h>
Servo doorServo; //сервопривод крышки
Servo handServo; //сервопривод руки
Bounce bouncer = Bounce(12, 40); //создаем экземпляр класса Bounce для 12 вывода тумблера
int pos = 0; //переменная начальной позиции
int pos1door = 70; //начальное положение сервопривода крышки
int pos2door = 30; //конечное положение сервопривода крышки
int pos1hand = 10; //начальное положение сервопривода руки
int pos2hand = 160; //конечное положение сервопривода руки
int r; //случайное число, от которого зависит вариант выключения тумблера
#define LED_PIN 11 // номер выхода,подключенного к светодиоду
int ledState = LOW; // этой переменной устанавливаем состояние светодиода
long previousMillis = 0; // храним время последнего переключения светодиода
#define INTERVAL 30UL // интервал между включение/выключением светодиода
void setup()
{
pinMode(LED_PIN, OUTPUT);
pinMode(12, INPUT); //переключаем 12 вывод в режим входа
digitalWrite(12, 1); //включаем на нем подтягивающий резистор
Serial.begin(9600); //установка порта на скорость 9600 бит/сек. Для отладки. Потом можно убрать.
doorServo.attach(9); //назначаем сервопривод крышки на пин 9
handServo.attach(10); //назначаем сервопривод руки на пин 10
doorServo.write(pos1door); //устанавливаем в начальную позицию сервопривод крышки
handServo.write(pos1hand); //устанавливаем в начальную позицию сервопривод руки
}
void loop()
{
if (bouncer.update()) {
if (bouncer.read()==0) { //если кнопка нажата
{
r = random(0,11); //генерируем случайное число jn 0 до 10
Serial.println®;
if (r == 0) { move_0(); } //вызов функции по случайному числу
else if (r == 1) { move_1(); }
else if (r == 2) { move_2(); }
else if (r == 3) { move_3(); }
else if (r == 4) { move_4(); }
else if (r == 5) { move_5(); }
else if (r == 6) { move_0(); }
else if (r == 7) { move_1(); }
else if (r == 8) { move_4(); }
else if (r == 9) { move_3(); }
else if (r == 10) { move_0(); }
}
}
}
}
// Библиотека функций. Общий принцип: открыть крышку - высунуть руку и выключить тумблер - убрать руку - закрыть крышку
//а уж вариантов как это красиво обставить................
void move_0(){ //простой вариант: открыли-выключили-закрыли
//открываем крышку
for(pos = pos1door; pos >= pos2door; pos -=3)
{
doorServo.write(pos);
delay(15);
}
//высовываем руку
for(pos = pos1hand; pos <= pos2hand; pos +=3)
{
handServo.write(pos);
delay(15);
}
//убираем руку
for(pos = pos2hand; pos >= pos1hand; pos -=3)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
for(pos = pos2door; pos <= pos1door; pos +=3)
{
doorServo.write(pos);
delay(15);
}
}
void move_1(){ //простой вариант 2: открыли-выключили-закрыли
//открываем крышку
for(pos = pos1door; pos >= pos2door; pos -=1)
{
doorServo.write(pos);
delay(15);
}
delay(1000);
//высовываем руку
for(pos = pos1hand; pos <= pos2hand; pos +=1)
{
handServo.write(pos);
delay(15);
}
//убираем руку
for(pos = pos2hand; pos >= pos1hand; pos -=5)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
for(pos = pos2door; pos <= pos1door; pos +=5)
{
doorServo.write(pos);
delay(15);
}
}
void move_2(){ //задумчивый вариант 2: приоткрыли-закрыли-открыли-выключили-закрыли
//открываем крышку
for(pos = pos1door; pos >= pos2door+15; pos -=5)
{
doorServo.write(pos);
delay(15);
}
delay(1000);
//закрываем крышку
for(pos = pos2door+15; pos <= pos1door; pos +=5)
{
doorServo.write(pos);
delay(15);
}
delay(1000);
//открываем крышку
for(pos = pos1door; pos >= pos2door; pos -=5)
{
doorServo.write(pos);
delay(15);
}
//высовываем руку
for(pos = pos1hand; pos <= pos2hand+2; pos +=5)
{
handServo.write(pos);
delay(15);
}
//убираем руку
for(pos = pos2hand+2; pos >= pos1hand; pos -=5)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
for(pos = pos2door; pos <= pos1door; pos +=5)
{
doorServo.write(pos);
delay(15);
}
}
void move_3(){ //дерганый вариант: приоткрыли-подергали - закрыли-открыли-выключили-закрыли
//открываем крышку
for(pos = pos1door; pos >= pos2door+15; pos -=1)
{
doorServo.write(pos);
delay(50);
}
delay(500);
//дергаем крышку
for(int i=1; i <=8; i ++)
{
doorServo.write(pos2door+18);
delay(80);
doorServo.write(pos2door+15);
delay(80);
static unsigned long previousMillis = 0;
if(millis() - previousMillis > INTERVAL) {
previousMillis = millis();
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
}
}
delay(500);
//закрываем крышку
for(pos = pos2door+15; pos <= pos1door; pos +=1)
{
doorServo.write(pos);
delay(50);
}
delay(1000);
//открываем крышку
for(pos = pos1door; pos >= pos2door; pos -=5)
{
doorServo.write(pos);
delay(15);
}
//высовываем руку
for(pos = pos1hand; pos <= pos2hand+2; pos +=5)
{
handServo.write(pos);
delay(15);
}
//убираем руку
for(pos = pos2hand+2; pos >= pos1hand; pos -=5)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
for(pos = pos2door; pos <= pos1door; pos +=5)
{
doorServo.write(pos);
delay(15);
}
}
void move_4(){
//открываем крышку
delay(2000);
for(pos = pos1door; pos >= pos2door+15; pos -=5)
{
doorServo.write(pos);
delay(50);
}
delay(500);
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
delay(2000);
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
delay(500);
//закрываем крышку
for(pos = pos2door+15; pos <= pos1door; pos +=5)
{
doorServo.write(pos);
delay(50);
}
delay(1000);
//открываем крышку не полностью
for(pos = pos1door; pos >= pos2door+15; pos -=1)
{
doorServo.write(pos);
delay(50);
}
delay(2000);
//открываем крышку полностью
for(pos = pos2door+15; pos >= pos2door; pos -=1)
{
doorServo.write(pos);
delay(15);
}
//высовываем руку
for(pos = pos1hand; pos <= pos2hand-35; pos +=1)
{
handServo.write(pos);
delay(35);
}
delay(1000);
//высовываем руку
for(pos = pos2hand-35; pos <= pos2hand+3; pos +=4)
{
handServo.write(pos);
delay(15);
}
//убираем руку
for(pos = pos2hand+3; pos >= pos1hand; pos -=7)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
for(pos = pos2door; pos <= pos1door; pos +=7)
{
doorServo.write(pos);
delay(15);
}
delay(500);
//открываем крышку
for(pos = pos1door; pos >= pos2door+20; pos -=5)
{
doorServo.write(pos);
delay(50);
}
delay(300);
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
delay(500);
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
delay(100);
//закрываем крышку
for(pos = pos2door+20; pos <= pos1door; pos +=1)
{
doorServo.write(pos);
delay(50);
}
}
void move_5(){
//возня
for(int i=1; i <=2; i ++)
{
for(pos = pos1door; pos <= pos1door+45; pos +=5)
{
doorServo.write(pos);
delay(50);
}
for(pos = pos1door+45; pos >= pos1door; pos -=5)
{
doorServo.write(pos);
delay(50);
}
delay(100);
}
//дергаем крышку
for(int i=1; i <=3; i ++)
{
doorServo.write(pos1door-6);
delay(80);
doorServo.write(pos1door-3);
delay(80);
}
delay(300);
//открываем крышку
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
for(pos = pos1door; pos >= pos2door+25; pos -=5)
{
doorServo.write(pos);
delay(50);
}
delay(500);
//открываем крышку
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
for(pos = pos2door+25; pos >= pos2door+10; pos -=5)
{
doorServo.write(pos);
delay(50);
}
//открываем крышку
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
for(pos = pos2door+10; pos >= pos2door-5; pos -=2)
{
doorServo.write(pos);
delay(50);
}
//высовываем руку
for(pos = pos1hand; pos <= pos2hand-35; pos +=9)
{
handServo.write(pos);
delay(35);
}
delay(1000);
//убираем руку
for(pos = pos2hand-35; pos >= pos2hand-70; pos -=1)
{
handServo.write(pos);
delay(15);
}
delay(1000);
//высовываем руку
for(pos = pos2hand-70; pos <= pos2hand+3; pos +=9)
{
handServo.write(pos);
delay(15);
}
delay(50);
//убираем руку
for(pos = pos2hand+3; pos >= pos1hand; pos -=7)
{
handServo.write(pos);
delay(15);
}
//закрываем крышку
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
for(pos = pos2door-5; pos <= pos1door+3; pos +=5)
{
doorServo.write(pos);
delay(50);
}
}
+47 |
2530
89
|
+23 |
1969
52
|
+169 |
4019
48
|
+25 |
2008
26
|
А с другой стороны, не специально, но знатно получилось)))
Продраться сквозь не смог…
В коде в setup() напрашивается INPUT_PULLUP
Составляющие движений в функциях move_XX() у вас описаны однотипно. Просятся в отдельную функцию, чтобы у вас были пачки инструкций в одну строку, например,
step(from;to;increment);
step(from;to;increment);
step(from;to;increment);
так дебажить проще. Одинаковые значения (например, паузы между движениями) — в константы надо бы убрать, чтобы можно было поправить в одном месте.
Сезон отработали 8 штук — все норм.
такой клапан у меня один есть, сопротивление потоку у него меньше, чем у тех, которые с «прямым приводом», но всё-же шаровый это шаровый… один минус — дёргать его надо периодически, чтоб не закисал. Но это уже программная проблема.
И, кстати, по цене. Если надо много (10), то клапаны выйдут в ~22000 р., и жрать будут все разом открытые ампер 10-15. А с сервами выйдет 300 р. серва + кран (300-500), итого 6000-8000 р. если к каждой мелкую тиньку добавить. чтобы получить просто «открыть закрыть» то будет на 100 р. дороже каждая.
Как-то боретесь с закисаниями кранов? Я читал что их надо хотя б раз в пол годика дергать, чтоб не закисли… Хотя если у вас на колонке — то наверное они чаще перемещаются, чем если делать защиту от протеканий на вводе…
======о======
|-------------------|
======O=====
Т.е. верхее коромысло на кран на штуцер закрепляется, нижнее коромысло на двигатель, по края тяги. У меня там краны на 60 градусов, вполне себе хватало.
Сейчас имея 3D принтер все переделано и ось сервы соосна с краном и просто проставка.
1. Встроенные резисторы довольного большого номинала и поэтому довольно помехонеустойчивые
2. Встроенные резисторы, бывают, сгорают. И об этом вы не узнаете.
Так что для надежности лучше проверенные 10к на VCC
наигралсявсё сделано. И три GSM-модуля ждут своего часа. Пока только поигрался с смс-ками и отложил. Торопиться не надо.Типа, работать не хочет.
потребление батареи
Нет ничего полезнее бесполезных вещей, потому как только бесполезные вещи делаются исключительно для души, так что + явно заслужен :)
Так что автору есть куда стремиться =)
+ однозначно.
Замечание за знание английского. «Useless» пишется немного не так, как вы написали.
Лайфхак: пожно написать кирилицей «бесполезная коробка». Ютуб от этого не сломается.
спасибо за желание начать разбираться!
заменить на #define pos1door 70
Следом использование типов переменных. uint16_t там где это не надо не следует. Есть uint8_t.
Все используемые пины описывайте
Так в случае если пин переедет не надо его искать.
Описываем как отдельные функции:
Вызываем скажем так:
moveServo(-1);
moveServo(1);
Если добавить ссылки и указатели, то можно еще и для всех серв 1 функцию намаслякать.
Пользоваться random() без randomSeed() не интересно, ибо всегда генерируется одна последовательность по дефолту.
И подтягивающие резистры желательно юзать внешние. Внутренние хоть и есть, но если он умрет — будет не айс…
«u» в начале означает unsigned — беззнаковый, то есть int8_t может быть от -128 до 127, uint8_t от 0 до 255. Помните стишок про 10 программистов? :)
А вообще все эти _t нафиг не нужны, пока вас не беспокоят проблемы портируемости и кроссплатформенности.
int16_t — 2 байта -32 768 до 32 767
uint8_t — 1 байт — 0 до 255
а скажем int8_t — 1 байт — -128 до 127
int — челых 4 байта! -2 147 483 648 до 2 147 483 647 (он же int32_t)
u — unsigned — т.е. без знака
Скажем мы знаем что положение сервы может меняться только от 0 до 180. Значит нам вполне хватает uint8_t
habrahabr.ru/post/156593/ — вот популярно
тут кратко и таблично применительно к AVR (а значит к ардуино):
digitalchip.ru/skolko-vesyat-tipyi-dannyih
кратко:
char 1 (8) -128… 127
unsigned char 1 (8) 0… 255
signed char 1 (8) -128… 127
int 2 (16) -32768… 32767
unsigned int 2 (16) 0… 65535
signed int 2 (16) -32768… 32767
short int 2 (16) -32768… 32767
unsigned short int 2 (16) 0… 65535
signed short int 2 (16) -32768… 32767
long int 4 (32) -2147483648… 2147483647
unsigned long int 4 (32) 0… 4294967295
signed long int 4 (32) -2147483648… 2147483647
float 4 (32) 3.4Е-38… 3.4Е+38
double 4 (32) 3.4Е-38… 3.4Е+38
long double 10 (80) 3.4Е-4932… 3.4Е+4932
производные:
int8_t signed char
uint8_t unsigned char
int16_t signed int
uint16_t unsigned int
int32_t signed long int
uint32_t unsigned long int
int64_t signed long long int
uint64_t unsigned long long int
__int16 работает в Visual Studio 2015 — там так и написано
табличка для тех кто там пишет, а остальные, в том числе и автор обзора, пишут не там
у майкрософт свои стандарты — отличающиеся от других, туда нужно ссылаться если разработка идет в той среде
хотя в AVR GCC int 2 байта -32768… 32767.
Так и представляю: автор написал программу для Arduino Mini, которая ждет условия (переменная_int>100000), спрашивает «почему не работает?» на той же амперке, ему намекают на тип данных, а он — вот же, сам Майкрософт написал!
Иногда лучше отсутствие данных (тогда человек на оф.сайте найдет правильные), чем ложная уверенность в неправильных данных.
А тут для себя понял что чтобы не теряться надо юзать XintN_t и все сразу становится предельно ясно )
Добавлением одного (ОДНОГО, КАРЛ!) индекса я свел его до получаса
Если в табличках 100 записей то это будет отлично работать и без него. даже быстрее чем с ним.
Потом таблички некисло разрастаются и почему-то без него начинает работать хреново :-)
Ну и самый главный принцип: Ты сделай, чтоб хоть как-то работало, потому что
завтравчера нам надо это сдавать :-)const uint8_t n1=1;
uint8_t n2=2;
n2+=n1 //тут мы обращаемся к ячейке памяти где хранится n1
n2+=n1 //и тут мы обращаемся к ячейке памяти где хранится n1
а с #define… Смотрите сами:
#define n1 1
uint8_t n2=2;
n2+=n1 //тут мы обращаемся к ячейке памяти где хранится 1
n2+=n1 //а тут мы обращаемся к другой ячейке памяти где хранится 1
Выходит define ложная катавасия в плане экономии ресурсов
Плюс (нам это не грозит) при отладке скажем при #define test 123 в случае если будет проблема с этими 123, то отладчик покажет 123, а не test и получистя не будет известно от куда взялось это 123. Т.е. скажем когда
мы не будем знать где произошло это деление на 0 точно. В таком случае в C++ (в С нет) inline функции. Блин… я программист, меня год учили C++, а я сдал последнюю лабу за 1 месяц и не ходил на занятия целый курс… Я много пропустил )
Если интересно, вот большая статья про них.
Удивительно, но в плюсах тоже частенько можно встретить, причём не только в роли #include guard. Последний особенно впечатливший меня пример — это реализация исключений в Poco. Нет, я всё понимаю, но взять и настрогать классов дефайнами…
Насчёт памяти вы правы, хотя мне кажется, что компилятор должен уметь оптимизировать такие вещи.
uint8_t n2=2;
n2+=n1;
«1» нигде не хранится, в том смысле в каком это понимаете вы — эта константа будет непосредственно в коде.
А вот если у нас n1 — это const uint8_t, тогда в коде у нас будет другая константа: адрес той ячейки памяти, в которой лежит значение «1». И добавится лишняя операция — чтение по данному адресу.
Хотя, компилятор может и соптимизировать это, сведя ситуацию к предыдущей. Кстати, интересный вопрос, каковы дефолтные опции компилятора в среде ардуино. Надо б посмотреть… :)
Константа непосредственно в коде (#define) — инструкция LDI www.gaw.ru/html.cgi/txt/doc/micros/avr/asm/ldi.htm — занимает 2 байта памяти программ и исполняется за 1 цикл, константа в оперативной памяти (const) — LDS www.gaw.ru/html.cgi/txt/doc/micros/avr/asm/lds.htm — 4 байта и 3 цикла + место в памяти данных под эту константу, константа в памяти программ (PROGMEM const) — LDM www.gaw.ru/html.cgi/txt/doc/micros/avr/asm/lpm.htm — 2 байта и 3 цикла (и еще команда загрузки в Z) + занимаемая под константу память программ. Так что если константа не более 255 — #define оптимален и с точки зрения экономии памяти программ, и памяти данных, и быстродействия.
имея опыт творчества и читая текст, вижу между строк сколько трудов, неудач, матов и, конечно, радости от работающей поделки пришлось на долю автора.
однозначно плюс…
а главное, когда пишешь — вроде и писать особо нечего — все очевидно (поле граблей после изготовления готового устройства — кажется только твоим, и у других, опять же ощущение, его не возникнет)
Посмотрите лучше в сторону гексаподов:
Плюсанул
Выкройка корпуса проста — простые формы. Размер не проектировался специально. Были листы фанеры, формата 200х315 толщиной 3 мм. Я его обрезал до размера 150х315 мм. Потом покромсал на полоски двух ширин: одна полоса шириной 59мм и четыре полосы шириной 64мм. Верхняя, нижняя и боковые крышки абсолютно одинаковые, 150х64мм. Зад и перед вырезаны из одной полосы, их размер 64х59мм. Крышка формируется легко — распиливаем одну полосу ножовкой по металлу. Ее толщина и есть зазор между подвижной и неподвижной крышками. Толкатель крышки в моем примере имеет размер 28х9мм с закругленными углами. «Руку» вырезал так. Приклеил серву на тонкий двусторонний скотч. Взял кусок толстого картона и накидал выкройку. Привинтил ее винтами и правил ножницами. Без подключения сервы. Просто двигал рукой. Центр отверстия для тумблера расположен на расстоянии 21мм от края, из-за которого вылетает рука. Размер руки нужно подбирать исходя из высоты тумблера и хода его рычажка.
У меня «рука» имеет такие размеры:
Поверьте, из картона заготовка вырезается на раз.
В моем случае, я ошибся с расположением сервы. Правильнее было бы установить не сбоку, а перед тумблером, вытянув ее вертикально. Тогда можно упростить «руку» до г-образной, и сделать ее тоньше.
Если захотите прикрепить какую-то нашлепку, для пущего дизайна, обязательно просверлите под ней дырку. Тогда у этой детали появится еще и сугубо практическое назначение.
В принципе, моторы у меня тоже с Али, могу и обзор запилить:)
Находил описание, как писать на карту, но хотелось бы изучить девайс последовательно. С чего начать, желательно на русском языке.
А 3 или 5В версия надо подбирать под GPS-модуль чтобы потом не добавлять согласование уровней. Скорей всего нужно будет 3-х вольтовую версию.
Может знающие подскажут, что лучше взять — arduino nano или micro?.. И еще вопрос — выдержат ли эти ардуино мощность серв? Это нужно в даташите на ардуино смотреть?
А если бы еще динамик с «рычанием» «гавканьем» и жалобными звуками добавили… ребетенка было бы не оторвать)))
Если бы наш дед соорудил такую штуку своему внуку… то привил бы страсть к такому творчеству надолго)))
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.