Код лагает. Помогите пожалуйста исправить

Тема в разделе "Arduino & Shields", создана пользователем iBoy, 20 янв 2017.

  1. iBoy

    iBoy Гик

    Помогите пожалуйста! Это очень срочно!

    Цель такая:

    Есть велотренажёр. Его задача - считывать вращение педалей и посылать сигнал на комп, от которого будет ехать вперёд гоночная машина. Причём ехать она должна тем быстрее, чем выше нагрузка(а значит и пульс). Также есть штраф на случай, если педали не крутят. И есть кнопка, которая отключает движение вперёд но не штраф. В итоге получается такая импровизированная приставка. Это всё представляется на конкурс, дедлайн уже совсем скоро - 28 января.

    Итого: есть код с вот такими задачами:

    - Он должен имитировать зажатие W(именно зажатие, не нажатие), если по порядку замыкаются герконы(посредством вращения магнита), подключённые к трём контактам.

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

    - В случае, если отпущена кнопка первое действие не выполнять

    - Если в течение некоторого времени не замыкаются герконы, он должен делать штраф в виде имитации зажатия S, A, D

    Проблема заключается в том, что, почему то, он через небольшое время он просто берёт и перестаёт что-либо делать - как будто в ардуино загрузили пустой код. Я пробовал менять номера пинов и сами ардуины. Я даже на всякий случай менял провода. Но это всё не помогло. Вот такой код:
    Код (C++):
    #include <TimerThree.h>                    
    //Это библиотека, которая позволяет использовать таймер с целью каждые n микросекунд обращаться к какой-то функции. Подробнее про неё можно почитать здесь: https://www.pjrc.com/teensy/td_libs_TimerOne.html

    #include <Keyboard.h>

    #define butPin 4
    #define DELT 75
    #define waiting1 10000
    #define waiting2 80
    #define waiting3 5500
    #define waiting4 9000
    #define chislitel 2000
    #define x 30
    #define pulsePin A0
    #define pokazatel_stepenyi 3

    unsigned long puls_ex;
    //saadsadsadsadsa
    unsigned long puls = 0;
    volatile int pulse[2] = {0, 0};
    volatile bool vect[2];
    volatile unsigned long lastTimerPuls = 0;
    volatile unsigned long currTimerPuls = 0;
    bool ochko[4];
    long t;
    int j;
    int i;
    //

    void setup() {
      Timer3.initialize(40000);
      Timer3.attachInterrupt(pulsFunction);
      Serial.begin(9600);
      for (i = 3; i < 12; i++) {
        pinMode(i, INPUT_PULLUP);
      }
      delay(waiting1);
    }
    //saddssadssadsad

    void loop() {
      bool flag = false;
      //Назовём флагом факт вращения магнита вокруг герконов
      for (i = 7; i < 10; i++) {
        if (digitalRead(i) == LOW) {
        //Если i-й геркон замкнут
          bool was_Active = !digitalRead(i + 1);
          //Тогда проверить, не замкнут ли ещё и i+1-й
          bool is_Active = false;
          //Назовём факт замкнутости i+1-го геркона именем is_Active
          for (j = 1; j <= x; j++) {
          //Этот цикл For является ожиданием, пока магнит не сдвинут
            if (digitalRead(i + 1) == LOW)
            {
              is_Active = true;
              break;
            }
            delay(waiting2);
          }
          if (is_Active != was_Active) {
          //Если магнит сдвинулся(т.е. i+1-й геркон поменял состояние)
            flag = true;
            //Считать, что магнит вращают
            Serial.println("Ok");
            //Сообщить об этом
            break;
          }
        }
      }
      if (flag)
      //Если таки магнит вращали
      {
        t = millis();
        //Засечь время(это дальше понадобится)
        puls_ex = 0;
        noInterrupts();
        //Дабы исключить возможность обращения к puls в момент присвония, запртим прерывания
        puls_ex = puls;
        //Назовём конечные обработанные показания датчика puls_ex
        interrupts();
        Serial.println(puls_ex);
        //Вывести конечнгые и бработанные показания датчика
        Serial.println(digitalRead(butPin));
        //Если нажат переключатель
        if (digitalRead(butPin) == LOW)
        //Отчитаться об этом
        {
          Serial.println("the button is high");
          Keyboard.press('w');
          //Зажать(именно зажать, а не нажать) "W"
          Serial.print(chislitel / (pow((puls_ex + 1), pokazatel_stepenyi)));
          //Вывести преобразование, которое произойдёт через несколько строк
          Serial.println(" secund ne budet nazhato W, a istcho papa lutshiy");//
          delay(waiting3);
          //В течение всего этого времени не будет зажато "W"
          Keyboard.release('w');
          //Отпустить "W"
          delay(1000 * chislitel / (pow((puls_ex + 1), pokazatel_stepenyi)));
          //Ждать до следующего нажатия определённое время. Единица добавляется для исключения деления на ноль
        }
      }
      else {
      //Если же магнит не вращали
        if (millis() - t >= waiting4) {
        //И с последнего момента вращения прошло достаточно времени
          shtraf();
          //Тогда штрафовать
        }
      }
    }

    void shtraf() {
    //Процедура штрафа
      for (j = 7; j <= 9; j++) {
      //Здесь идёт проверка на замкнутость каждого геркона
        if (digitalRead(j) == LOW) {
        //В случае, если геркон замкнут,
          ochko[j-6] = true;
          //Назовём этот факт очком этой цепи
        }
      }
      if ((ochko[1] == false) || (ochko[2] == false) || (ochko[3] == false)) {
      //Если хотя бы одна цепь не разу не замыкалась,
        Serial.println("shtrafuem");
        //Отчитаться
        Serial.println(ochko[1]);
        //В этой последующих двух строчках выводятся очки цепей с целью определить, что именно не замкнуто
        Serial.println(ochko[2]);
        Serial.println(ochko[3]);
        Keyboard.press('s');
        //В этой и послдующих семи строках происходит штраф - нажатие определённых кнопок
        delay(200);
        Keyboard.press('a');
        delay(300);
        Keyboard.release('a');
        Keyboard.press('d');
        delay(500);
        Keyboard.release('s');
        Keyboard.release('d');
        delay(100);
        shtraf();
      }
      if ((ochko[1] == true) && (ochko[2] == true) && (ochko[3] == true)) {
        //А если же все цепи уже замыкались
        ochko[1] = false;
        //На этой и двух последующих строках обнуляются все очки
        ochko[2] = false;
        ochko[3] = false;
        Serial.println("konets shtrafa");
        //Отчёт о конце штрафа
      }
    }

    void pulsFunction() {
      //Датчик пульса выдаёт показания в виде цифр, обозначающих, так сказть, кардиаграмму
      vect[0] = (pulse[1] > (pulse[0] + DELT));
      //Назовём vect[0] старое направление кардиограммы, pulse[0] - старые конкретные показания, pulse[1] - новые. Тогда,
      pulse[0] = pulse[1];
      //Обновить показания датчика
      pulse[1] = analogRead(pulsePin);
      //Считать показания датчика
      vect[1] = (pulse[1] > (pulse[0] + DELT));
      if (vect[0] && !vect[1]) {
      //Если старое направление кардиограмамы - вверх, а новое - вниз, т.е. произошёл пик кардиограммы
        currTimerPuls = millis();
        //Засечь время
        puls = (60000 / (currTimerPuls - lastTimerPuls + 5));
        /*По сути, пульс - это частота сердечных сокращений(а значит и обращений к нашей функции) в минуту,
        тоесть можно считать, что если 60 секунд разделить на время, прошедшее с последнего обращения, мы получим пульс*/

        lastTimerPuls = currTimerPuls;
        //Обновить временной счётчик
      }
    }
     
    Последнее редактирование: 20 янв 2017
  2. sslobodyan

    sslobodyan Гик

    Я так понял, что плохо работает именно определитель вращения. Смотрим по коду. Вы не написали, по коду разбираю, что герконы подключены к 7,8 и 9 пинам. Тогда вот этот код if (digitalRead(i + 1) == LOW) будет запрашивать 10 пин, что совсем не туда.
    Но я бы полностью переделал процедуру определения вращения. На велотренажере крутить педали в обратку не выйдет. Составляем таблицу возможных состояний герконов (опрашиваем каждый вход и собираем из битов байт). Состояний будет несколько: 011, 101, 110, 111(ни один не замкнут). Значит если предыдущее состояние было 101, а вновь считанное 110 (ну или другое, в зависимости какой геркон к какой ноге подключен), то педали крутят. Состояния 111 пропускаем. Идея понятна?
     
  3. ostrov

    ostrov Гуру

    Может вручную прерывание по таймеру запустить? Недоверяю я этим библиотекам. Опять же может быть третий таймер с чем то другим пересекается? И вот тут уверены, что нукцию без скобок надо указывать? Timer3.attachInterrupt(pulsFunction);
     
  4. sslobodyan

    sslobodyan Гик

    Да, без скобок. Это передача адреса функции.
     
  5. iBoy

    iBoy Гик

    Я, наверное, не очень хорошо объяснил. Всё работает, однако почему-то вырубается через несколько минут использования. А насчёт определения вращения - состояний больше:
    Назовём нулём замкнутость, а 1 - незамкнутость:
    111 - на практике его нет
    011
    101
    110
    001
    010
    100
    000 - его тоже на практике нет
    Те состояния, где замкнуто по два геркона - самые частые.
    И ещё - по каким-то мистическим причинам работает вот такой код(причём нормально и правильно):
    Код (C++):
    //
    //
    //
    //
    //
    //sasaadsaddsadsad
    #include <TimerThree.h>
    #include <Keyboard.h>

    #define butPin 4
    #define DELT 70
    #define waiting1 10000
    #define waiting2 80
    #define waiting3 6000
    #define waiting4 9000
    #define chislitel 2000
    #define x 30
    #define pulsePin A0
    #define pokazatel_stepenyi 3.2

    unsigned long puls_ex;
    //saadsadsadsadsa
    unsigned long puls = 0;
    int pulse[2] = {0, 0};
    bool vect[2];
    unsigned long lastTimerPuls = 0;
    unsigned long currTimerPuls = 0;
    bool ochko1;
    bool ochko3;
    bool ochko2;
    long t;
    int j;
    int i;
    //

    void setup() {
      Timer3.initialize(50000);
      Timer3.attachInterrupt(pulsFunction);
      Serial.begin(9600);
      for (i = 3; i < 12; i++) {
        pinMode(i, INPUT_PULLUP);
      }
      delay(waiting1);
    }
    //saddssadssadsad

    void loop() {
      bool flag = false;
      for (i = 7; i < 10; i++) {
        if (digitalRead(i) == LOW) {
          bool was_Active = !digitalRead(i + 1);
          bool is_Active = false;
          for (j = 1; j <= x; j++) {
            if (digitalRead(i + 1) == LOW)
            {
              is_Active = true;
            }
            delay(waiting2);
          }
          if (is_Active != was_Active) {
            flag = true;
            Serial.println("Ok");
            break;
          }
        }
      }
      if (flag)
      {
        t = millis();
        puls_ex = 0;
        for (int s = 1; s <= 45; s++) {
          puls_ex += puls;
        }
        puls_ex /= 45;
        Serial.println(puls_ex);
        Serial.println(digitalRead(butPin));
        if (digitalRead(butPin) == LOW)
        {
          Serial.println("the button is high");
          Keyboard.press('w');
          Serial.print(chislitel / (pow((puls_ex + 1), pokazatel_stepenyi)));
          Serial.println(" secund ne budet nazhato W, a istcho papa lutshiy");
          delay(waiting3);
          Keyboard.release('w');
          delay(1000 * chislitel / (pow((puls_ex + 1), pokazatel_stepenyi)));
        }
      }
      else {
        if (millis() - t >= waiting4) {
          shtraf();
        }
      }
    }

    void shtraf() {
      if (digitalRead(7) == LOW) {
        ochko1 = true;
        delay(100);
      }
      if (digitalRead(8) == LOW) {
        ochko2 = true;
        delay(100);
      }
      if (digitalRead(9) == LOW) {
        ochko3 = true;
        delay(100);
      }
      if ((ochko1 == false) || (ochko2 == false) || (ochko3 == false)) {
        Serial.println("shtrafuem");
        Serial.println(ochko1);
        Serial.println(ochko2);
        Serial.println(ochko3);
        Keyboard.press('s');
        delay(200);
        Keyboard.press('a');
        delay(300);
        Keyboard.release('a');
        Keyboard.press('d');
        delay(500);
        Keyboard.release('s');
        Keyboard.release('d');
        delay(1000);
        shtraf();
      }
      //
      if ((ochko1 == true) && (ochko2 == true) && (ochko3 == true)) {
        ochko1 = false;
        ochko2 = false;
        ochko3 = false;
        Serial.println("konets shtrafa");
      }
    }


    void pulsFunction() {
      vect[0] = (pulse[1] > (pulse[0] + DELT));
      pulse[0] = pulse[1];
      pulse[1] = analogRead(pulsePin);
      vect[1] = (pulse[1] > (pulse[0] + DELT));
      if (vect[0] && !vect[1]) {
        currTimerPuls = millis() + 1;
        puls = (60000 / (currTimerPuls - lastTimerPuls + 1));
        lastTimerPuls = currTimerPuls;
      }
    }
     
    Тоесть я вернул резервный вариант кода и он заработал, теперь всё не так срочно. Но я всё равно не понимаю, в чём прикол. Объясните пожалуйста. Заранее спасибо!
     
  6. iBoy

    iBoy Гик

    Я 7 пин подключил также и к 10-му, т.е. там перемычка. А вообще для недопускания вращения назад такой код и сделано. Впринципе, вращения назад он не считывает, что и требуется. Всё равно спасибо!
     
    Последнее редактирование: 20 янв 2017
  7. sslobodyan

    sslobodyan Гик

    1. Очень сомневаюсь, что здесь кто-то будет досконально разбирать весь ваш код и искать ошибку, причем не имея под руками самого железа.
    2. Я описал сам принцип как работают с вращающимися переключателями, будь то герконы или энкодеры. А какие там у вас положения получаются - я не знаю, механику я не вижу. Если есть одновременное замыкание нескольких герконов, это алгоритм не меняет. Составляйте карту состояний и по переходу из одного состояния в другое определяйте вращение и скорость. У вас система сильно инерционная, поэтому можно даже отслеживать всего 2-3 равноудаленных друг от друга состояния.
    3. Вижу, что у вас две задачи - вращение и пульс. А что именно не работает - вы не описали. Повесьте светодиоды в каждой задаче и по миганию определите какая из них работает, а какая висит.
     
  8. ostrov

    ostrov Гуру

    Уверены что ошибка в коде а не аппаратная? Может элементарно питания не хватает? Или что то перегревается?
     
  9. sslobodyan

    sslobodyan Гик

    Нет, конечно не уверен на 100%. Но если другой скетч работает, то вряд ли он добавил питания или пересоединил проводники.
     
  10. qwone

    qwone Гик

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

    sslobodyan Гик

    Не судите строго ТС, ему всего 15. Опыта еще не набрался.
     
  12. qwone

    qwone Гик

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