Реализация задержки через millis в разовой функции.

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

В голове появилась идея реализации управления контактом посредством aREST и библиотеки EtherCard.

Идея такова: Установлена камера, на python написан скрипт-обработчик аналитики, если получено значение 1 (доступ разрешен), то на arduino отправляется строка с GET запроса, которая должна на указанное количество (n) отключить реле или светодиод. Т.к. реле нет, балуюсь со светодиодом. Сразу скажу - скетч нагло скомуниздил и переписал чуток под себя, все работает отлично, НО не знаю как правильно реализовать задержку на указанный период, чтоб не положить контроллер. можно ли попросить совета у форума? Скетч прилагаю.

// Подключение библиотек.
#include <EtherCard.h>
#include <aREST.h>
aREST rest = aREST();

// Статический IP адрес модуля.
//static byte myip[] = { 192, 168, 0, 245 };
// IP адрес шлюза по умолчанию.
//static byte gwip[] = { 192, 168, 0, 1 };
// Mac-адрес модуля. Должен быть уникальным внутри сети.
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };

// Буфер приема/передачи TCP/IP.
// Значение 350 выбрано и для экономии памяти, и из-за того, что размер выходного буфера aREST одинаков.
byte Ethernet::buffer[350]; // Размер буфера приема/передачи TCP/IP.
BufferFiller bfill;

// Переменные.
int pinLed = 3;
volatile int n = 0;
uint32_t timer = 0;

// Функция настройки микросхемы.
void setup() {
  Serial.begin(9600);
  Serial.println(F("\n[Проверка DHCP]"));
  Serial.print("MAC: ");
  for (byte i = 0; i < 6; ++i) {
    Serial.print(mymac[i], HEX);
    if (i < 5)
      Serial.print(':');
  }
  
  Serial.println();
  // По умолчанию Slave Select пин - 10.
  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) {
    Serial.println(F("Failed to acCacess Ethernet controller"));
  }
  
  // Включение статического IP.
  //  Serial.println(F("Setting up DHCP"));
  //  if (!ether.staticSetup()) {
  //    Serial.println(F("Static IP failed"));
  //  }
  
  // Включение DHCP.
  Serial.println(F("Setting up DHCP"));
  if (!ether.dhcpSetup()) {
    Serial.println(F("DHCP failed"));
  }
  
  ether.printIp("My IP: ", ether.myip);
  ether.printIp("Netmask: ", ether.netmask);
  ether.printIp("GW IP: ", ether.gwip);
  ether.printIp("DNS IP: ", ether.dnsip);

  // Назначение функций URL:
  rest.function("led", led);
  rest.function("pindelay", pindelay);

  // Преднастройка GPIO (3):
  pinMode(pinLed, OUTPUT);

  // Пин по умолчанию включен:
  digitalWrite(pinLed, 1);
}

// Функция, организующая установку задержки по запросу.
// В терминале linux:
// $ curl http://"IP адрес модуля"/pindelay?mseconds=0
// где 0 - количество миллисекунд.
int pindelay(String mseconds) {
  Serial.print("pindelay: ");
  if (mseconds.length() != 0) {
    n = mseconds.toInt();
    Serial.println(n);
  }
}

// Функция управления пином.
// В терминале linux:
// $ curl http://"IP адрес модуля"/led?params=1
// где 1 - выполнение условия в функции.
int led (String params) {
  if (params.length() != 0) {
    digitalWrite(pinLed, 0);
    //тут нужна задержка на n миллисекунд.
    digitalWrite(pinLed, 1);
  }
}

void loop() {
  word len = ether.packetReceive();
  word pos = ether.packetLoop(len);
  if (pos) {
    bfill = ether.tcpOffset();
    char *data = (char *) Ethernet::buffer + pos;
    
    if (strncmp("GET /", data, 5) != 0) {
      // Неподдерживаемый метод запроса (POST или другие).
      bfill.emit_p(PSTR("Unsupported HTTP request."));
    } else {
      
      // vvvvvv------ REST вручную ------vvvvvv
      rest.handle_proto(data);
      char *res = rest.getBuffer();
      // Отправка HTTP-заголовка:
      bfill.emit_p(
        PSTR("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nPragma: no-cache\r\n\r\n")
      );
      // Отправка результата и обнуление буфера REST:
      rest.resetBuffer();
      rest.reset_status();
      // vvvvvv------ REST вручную ------vvvvvv\
      
    }
    ether.httpServerReply(bfill.position());    // Отправка HTTP ответа.
  }
  ether.packetLoop(ether.packetReceive());
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016
Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

Увы, у меня не получается вывести задержку разово при вызове функции, потому что я видимо что-то делаю не так, ибо я очень глуп. Не понимаю как поместить задержку в loop, чтоб не было разрывов по времени и чтоб все работало, при этом задержка через millis внутри функции не работает....

sadman41
Offline
Зарегистрирован: 19.10.2016

В хендлере поднять флаг типа ledActive, ledActivationStartTime = millis(), включить led.

В лупе проверять - если ledActive и заданный интервал прошёл (см. пример про blink w/o delay). то сбросить флаг ledActive и выключить led.

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

За такую подсказку невероятно огромное спасибо. В программировании я глуп, но вроде понял и почти добился чего хотел. Только вот добился я того, что у меня каждый раз отключает пин заданным значением. Я так понимаю, мне надо что-то добавить? Еще одно условие? Прилагаю скетч того, что вышло.

// Подключение библиотек.
#include <EtherCard.h>
#include <aREST.h>
aREST rest = aREST();

// Статический IP адрес модуля.
//static byte myip[] = { 192, 168, 0, 245 };
// IP адрес шлюза по умолчанию.
//static byte gwip[] = { 192, 168, 0, 1 };
// Mac-адрес модуля. Должен быть уникальным внутри сети.
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };

// Буфер приема/передачи TCP/IP.
// Значение 350 выбрано и для экономии памяти, и из-за того, что размер выходного буфера aREST одинаков.
byte Ethernet::buffer[350]; // Размер буфера приема/передачи TCP/IP.
BufferFiller bfill;

// Переменные.
unsigned long previousMillis = 0;
int ledPin = 3;
long interval = 0;
boolean flag;


// Функция настройки микросхемы.
void setup() {
  Serial.begin(9600);
  Serial.println(F("\n[Проверка DHCP]"));
  Serial.print("MAC: ");
  for (byte i = 0; i < 6; ++i) {
    Serial.print(mymac[i], HEX);
    if (i < 5)
      Serial.print(':');
  }

  Serial.println();
  // По умолчанию Slave Select пин - 10.
  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) {
    Serial.println(F("Failed to acCacess Ethernet controller"));
  }

  // Включение статического IP.
  //  Serial.println(F("Setting up DHCP"));
  //  if (!ether.staticSetup()) {
  //    Serial.println(F("Static IP failed"));
  //  }

  // Включение DHCP.
  Serial.println(F("Setting up DHCP"));
  if (!ether.dhcpSetup()) {
    Serial.println(F("DHCP failed"));
  }

  ether.printIp("My IP: ", ether.myip);
  ether.printIp("Netmask: ", ether.netmask);
  ether.printIp("GW IP: ", ether.gwip);
  ether.printIp("DNS IP: ", ether.dnsip);

  // Назначение функций URL:
  rest.function("led", led);
  rest.function("pindelay", pindelay);

  // Преднастройка GPIO (3):
  pinMode(ledPin, OUTPUT);

  // Пин по умолчанию включен:
  digitalWrite(ledPin, 1);
}

// Функция, организующая установку задержки по запросу.
// В терминале linux:
// $ curl http://"IP адрес модуля"/pindelay?mseconds=0
// где 0 - количество миллисекунд.
int pindelay(String mseconds) {
  Serial.print("pindelay: ");
  if (mseconds.length() != 0) {
    interval = mseconds.toInt();
    Serial.println(interval);
  }
}

// Функция управления пином.
// В терминале linux:
// $ curl http://"IP адрес модуля"/led?params=1
// где 1 - выполнение условия в функции.

int led (String params) {
  if (params.length() != 0) {
    flag = 1;
    digitalWrite(ledPin, 0);
  }
}


void loop() {
  unsigned long currentMillis = millis();
  word len = ether.packetReceive();
  word pos = ether.packetLoop(len);
  if (pos) {
    bfill = ether.tcpOffset();
    char *data = (char *) Ethernet::buffer + pos;

    if (strncmp("GET /", data, 5) != 0) {
      // Неподдерживаемый метод запроса (POST или другие).
      bfill.emit_p(PSTR("Unsupported HTTP request."));
    } else {

      // vvvvvv------ REST вручную ------vvvvvv
      rest.handle_proto(data);
      char *res = rest.getBuffer();
      // Отправка HTTP-заголовка:
      bfill.emit_p(
        PSTR("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nPragma: no-cache\r\n\r\n")
      );
      // Отправка результата и обнуление буфера REST:
      rest.resetBuffer();
      rest.reset_status();
      // vvvvvv------ REST вручную ------vvvvvv\

    }
    ether.httpServerReply(bfill.position());    // Отправка HTTP ответа.
  }
  if (flag == 1 & currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    //previousMillis = currentMillis;
    digitalWrite(ledPin, 1);
    flag = 0;
    previousMillis = currentMillis;
  }
  ether.packetLoop(ether.packetReceive());
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Засекать время старта нужно в момент активации светодиода, там же где устанавливается flag

В приведённом коде этого нет, зато есть лишняя строка #128

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

Светодиод активен всегда с момента включения ардуинки и выключается в момент вызова функции.

Если уберу строку #128, то не срабатывает включение светодиода. Если перенести currentMillis = millis(); в функцию, то светодиод при каждом запросе меняет состояние. Хоть вы и говорите так, однако, если я правильно вас понимаю и делаю по вашим словам, то результат отличается.

sadman41
Offline
Зарегистрирован: 19.10.2016

Feronos пишет:

Если уберу строку #128, то не срабатывает включение светодиода. Если перенести currentMillis = millis(); в функцию, то светодиод при каждом запросе меняет состояние. Хоть вы и говорите так, однако, если я правильно вас понимаю и делаю по вашим словам, то результат отличается.

ОК. Не трогаем REST, начинаем с простого: как определить факт истечения временного промежутка, какие три параметра для этого нужны?

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

Текущее время, предыдущее значение и интервал?

sadman41
Offline
Зарегистрирован: 19.10.2016

Feronos пишет:

Текущее время, предыдущее значение и интервал?

Который из этих трех параметров определяет начало течения временного промежутка?

Какой переменной он соответствует в примере "blink w/o delay"?

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

currentMillis - начало отсчета с момента включения.
interval - интервал, как бы очевидно это ни было
previousMillis - предыдущее значение currentMillis

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, то что переменные означают - понятно. Вопрос в том, которая из них определяет начало течения временного промежутка.

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

ну я так понимаю это должна быть currentMillis?

sadman41
Offline
Зарегистрирован: 19.10.2016

Вот поэтому у вас всё и включается само по себе.

Чтобы понять, сколько времени прошло, нужно из текущего времени вычесть начальное - так?

В формуле "currentMillis - previousMillis >= interval" где начальное время?

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

Извиняюсь, но если я правильно понял, вы намекаете что нужна еще 1 переменная?

sadman41
Offline
Зарегистрирован: 19.10.2016

Нет, я не намекаю. Дело там в одной строке кода, но я хочу, чтобы вы сами поняли, что делаете неправильно. 

Ещё один пример: на работу вы пришли в 8, до обеда надо отработать 4 часа. Сейчас 13:00 - уже можно уходить на обед? Как вы это поняли? Прямо вот напишите цифирями условие для if()

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

if(13 - 8 >= 4)
  {
    можно на обед
  }

Правильно?

sadman41
Offline
Зарегистрирован: 19.10.2016

Пока правильно. 

Теперь сравниваем "if(13 - 8 > 4)" с "currentMillis - previousMillis >= interval" и ещё раз отвечаем на вопрос - какая переменная отражает начальное время работы?

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

А, это значит что нужно указать previousMillis в момент выполнения функции, так?

sadman41
Offline
Зарегистрирован: 19.10.2016

bingo.

Только previousMillis назвать понятней - как ledActivationStartTime, например.

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020
// Подключение библиотек.
#include <EtherCard.h>
#include <aREST.h>
aREST rest = aREST();

// Статический IP адрес модуля.
//static byte myip[] = { 192, 168, 0, 245 };
// IP адрес шлюза по умолчанию.
//static byte gwip[] = { 192, 168, 0, 1 };
// Mac-адрес модуля. Должен быть уникальным внутри сети.
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };

// Буфер приема/передачи TCP/IP.
// Значение 350 выбрано и для экономии памяти, и из-за того, что размер выходного буфера aREST одинаков.
byte Ethernet::buffer[350]; // Размер буфера приема/передачи TCP/IP.
BufferFiller bfill;

// Переменные.
unsigned long ledActivationStartTime = 0;
int ledPin = 3;
long interval = 0;
boolean flag;


// Функция настройки микросхемы.
void setup() {
  Serial.begin(9600);
  Serial.println(F("\n[Проверка DHCP]"));
  Serial.print("MAC: ");
  for (byte i = 0; i < 6; ++i) {
    Serial.print(mymac[i], HEX);
    if (i < 5)
      Serial.print(':');
  }

  Serial.println();
  // По умолчанию Slave Select пин - 10.
  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) {
    Serial.println(F("Failed to acCacess Ethernet controller"));
  }

  // Включение статического IP.
  //  Serial.println(F("Setting up DHCP"));
  //  if (!ether.staticSetup()) {
  //    Serial.println(F("Static IP failed"));
  //  }

  // Включение DHCP.
  Serial.println(F("Setting up DHCP"));
  if (!ether.dhcpSetup()) {
    Serial.println(F("DHCP failed"));
  }

  ether.printIp("My IP: ", ether.myip);
  ether.printIp("Netmask: ", ether.netmask);
  ether.printIp("GW IP: ", ether.gwip);
  ether.printIp("DNS IP: ", ether.dnsip);

  // Назначение функций URL:
  rest.function("led", led);
  rest.function("pindelay", pindelay);

  // Преднастройка GPIO (3):
  pinMode(ledPin, OUTPUT);

  // Пин по умолчанию включен:
  digitalWrite(ledPin, 1);
}

// Функция, организующая установку задержки по запросу.
// В терминале linux:
// $ curl http://"IP адрес модуля"/pindelay?mseconds=0
// где 0 - количество миллисекунд.
int pindelay(String mseconds) {
  Serial.print("pindelay: ");
  if (mseconds.length() != 0) {
    interval = mseconds.toInt();
    Serial.println(interval);
  }
}

// Функция управления пином.
// В терминале linux:
// $ curl http://"IP адрес модуля"/led?params=1
// где 1 - выполнение условия в функции.

int led (String params) {
  if (params.length() != 0) {
    ledActivationStartTime = millis();
    flag = 1;
    digitalWrite(ledPin, 0);
    Serial.println(ledActivationStartTime);
  }
}


void loop() {

  word len = ether.packetReceive();
  word pos = ether.packetLoop(len);
  if (pos) {
    bfill = ether.tcpOffset();
    char *data = (char *) Ethernet::buffer + pos;

    if (strncmp("GET /", data, 5) != 0) {
      // Неподдерживаемый метод запроса (POST или другие).
      bfill.emit_p(PSTR("Unsupported HTTP request."));
    } else {

      // vvvvvv------ REST вручную ------vvvvvv
      rest.handle_proto(data);
      char *res = rest.getBuffer();
      // Отправка HTTP-заголовка:
      bfill.emit_p(
        PSTR("HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nPragma: no-cache\r\n\r\n")
      );
      // Отправка результата и обнуление буфера REST:
      rest.resetBuffer();
      rest.reset_status();
      // vvvvvv------ REST вручную ------vvvvvv
    }
    ether.httpServerReply(bfill.position());    // Отправка HTTP ответа.
  }
  if (flag == 1 && millis() - ledActivationStartTime >= interval) {
    digitalWrite(ledPin, 1);
    flag = 0;
  }
  ether.packetLoop(ether.packetReceive());
}

Все работает отлично. Спасибо огромное)

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

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

sadman41
Offline
Зарегистрирован: 19.10.2016

index.php?led=1&time=1230&color=black

Feronos
Feronos аватар
Offline
Зарегистрирован: 19.02.2020

Т.е. нужен обработчик в php? Или я опять не понимаю вас?))

sadman41
Offline
Зарегистрирован: 19.10.2016

Нужно передавать запрос такого вида. Как вы там к ардуине обращаетесь (URI) ? Вот приделайте к данному запросу "переменные" в таком формате.