Фиксированное точное время выполнения loop

Если getTemperatureb блокирующий, то луп короче его выполнения получить невозможно.

Извиняюсь, здесь не подумал, что всё так серьёзно))
В этом случае (ИМХО), нет возможности задать более-менее точный интервал. Остаётся надеяться на точность RC генератора в режиме SLEEP

Немного улучшить дело можно так:
(не проверял, т.к. не на чем)

Спойлер
#include "lgt_LowPower.h"
#include "FineOffset.h"
#include <Wire.h>
#include <AHTxx.h>
#define TX_PIN 2
#define DEVICE_ID 1235

//AHT20 aht20;
AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR);
FineOffset tx(TX_PIN);
unsigned long timer1;
float temp;

void setup() {
  timer1 = millis();
  
  CLKPR = 1<<PMCE;
  CLKPR = 0b00000100;// не уверен, что нужны эти строчки, но 
  //ни на чём не настаиваю, т.к. не могу проверить
  // timer1 = millis(); //как вариант, эта строка здесь
  Wire.begin();
  aht20.begin();
  temp = aht20.readTemperature();
}
void loop() {
  
  if (millis() - timer1 > 1000) {
 
    tx.send(DEVICE_ID, temp);
    
  for (int i=0; i<2; i++) {
    LowPower.powerDown(SLEEP_16S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_128MS, ADC_OFF, BOD_OFF);
  }
  }
}

Я не понял на каком этапе у вас сейчас затруднения. Т.к. в приведенных вариантах кода нет комментариев, зачем это и почему.
Если надо просто иметь фиксированное время этапа то можно сделать вида

#define TIME_1 200  // 
void loop() {
  // Кусок кода который должен выполнятся фиксированное время TIME_1 200 мс
  millis_1 = millis();  // Время начала
  1 Получение температуры (длительность меняется например от 50 до 150)
  2 Отправка результатов по Serial
  Serial.flush(); // ждем окончание отправки по сериал.
  //Общее время 1 + 2 (включая окончание отправки) должно быть меньше TIME_1
  //для перестраховки проверим это.
  if (millis() - millis_1 > TIME_1) { // если не уложились в TIME_1 то сообщаем
    Serial.print("TIME_1 Error ");
    Serial.println(millis() - millis_1);
    Serial.flush(); // ждем окончание отправки по сериал.
  }
  while (millis() - millis_1 < TIME_1) {}; // Ждем окончание TIME1
  // Конец куска кода который должен выполнятся фиксированное время

  4 Команда спать.
}

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

Не приходилось работать с этой библиотекой , поэтому поверил на слово.

Но теперь не уверен ,что это так, иначе не работали бы циклы

for (int i=0; i<2; i++) {
    LowPower.powerDown(SLEEP_16S, ADC_OFF, BOD_OFF);
  }

а сразу же, после первого прохода, проц уходил бы в ресет, и, начинал бы стартовать с “чистого листа”.
Посмотрел в библиотеку - так и есть.
Чтобы “отрубить чип полностью”, есть другая функция -

LowPower.deepSleep2(SLEEP_1S);

Можно легко проверить, запустив скетч, (подправить под lgt)

Спойлер
uint32_t timer1;
uint32_t timer2;

void setup() {
  timer1 = millis();//начало отсчёта
  wdt_enable(WDTO_1S);
  Serial.begin(9600);
  delay(1000);//время для усл. датчика

  while (millis() - timer1 < 1500); //ждём, пока пройдёт 1.5 сек
  timer2 = millis() - timer1; 
  Serial.println(timer2);//печатаем время с начала работы
  delay(100);//задержка для печати
  LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
  Serial.println(millis() - timer2);
}

void loop(void)  {

}

Если нижняя строчка в сетап отобразится - значит происходит выход из сна, и, продолжается выполнение программы

Это я собственно к тому, что мой скетч из #22, скорее всего нерабочий.

P.S.
Пока, лучше варианта не вижу, разве совсем всё переделывать

Спойлер
#include "lgt_LowPower.h"
#include "FineOffset.h"
#include <Wire.h>
#include <AHTxx.h>
#define TX_PIN 2
#define DEVICE_ID 1235

//AHT20 aht20;
AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR);
FineOffset tx(TX_PIN);
float temp;

void setup() {
  CLKPR = 1<<PMCE;
  CLKPR = 0b00000100;
  Wire.begin();
  aht20.begin();
}

void loop() {
 
  static uint32_t timer1 = 0;
  static bool start = true;
  
 if(start)
  {
  timer1 = millis();
  start = false;
  temp = aht20.readTemperature();// //датчик читаем здесь
  }

  if (millis() - timer1 > 1000 && !start) {
    tx.send(DEVICE_ID, temp);//передаём показания датчика
   
  for (int i=0; i<2; i++) {
    LowPower.powerDown(SLEEP_16S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_128MS, ADC_OFF, BOD_OFF);
  }
    start = true;
    Serial.println(millis() - timer1);
  }
}

Добрый вечер, уважаемые!
Спасибо за такое живое участие в “проблеме” и простите за нечастое посещение форума (дома жена, дети сопливые…).

Попробую по порядку:

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

Пост 23
С вашим куском еще не разбирался, обязательно позже посмотрю.
С потреблением проблем нет, в момент отправки (занимает в среднем 0,25 секунды) ~ 16мА, во время сна оставшиеся 47,75 сек - единицы мкА)

Пост 24
Да, на счет отрубания MCU вы правы, отрубается он только в deepSleep2, видимо до туда я не дочитал, хотя подозревал это, ведь циклы то работали в powerDown.
Проверить работу через сериал не получается, монитор в упор не видит ее на рекомендованных 57600, буду пробовать через putty, т.к. надо разбираться с “другой проблемой”.
Код отсюда обязательно попробую.

Ну и замеры с кодом из 22 поста:

21:47:03.900 -> ID: 1235, Temp: 31.4
21:47:51.889 -> ID: 1235, Temp: 31.4	//47.989
21:48:39.869 -> ID: 1235, Temp: 31.4
21:50:15.831 -> ID: 1235, Temp: 31.4
21:51:03.837 -> ID: 1235, Temp: 31.4	//48.006
21:52:39.856 -> ID: 1235, Temp: 31.4	
21:53:27.845 -> ID: 1235, Temp: 31.4	//47.989
21:55:51.855 -> ID: 1235, Temp: 31.4
21:56:39.854 -> ID: 1235, Temp: 31.4	//47.999
21:57:27.877 -> ID: 1235, Temp: 31.4	//48.023
21:58:15.854 -> ID: 1235, Temp: 31.4	//47.977
21:59:51.867 -> ID: 1235, Temp: 31.4	
22:01:27.922 -> ID: 1235, Temp: 31.4
22:03:03.920 -> ID: 1235, Temp: 31.4
22:03:51.934 -> ID: 1235, Temp: 31.4	//48.014
22:06:55.756 -> ID: 1235, Temp: 31.4
22:07:43.780 -> ID: 1235, Temp: 31.4	//48.024
22:08:31.783 -> ID: 1235, Temp: 31.4	//48.003
22:09:19.777 -> ID: 1235, Temp: 31.4	//47.994

Повторяемость очень хорошая, но на лицо две проблемы:

  1. Разбег между результатами 47* и 48* связан скорее всего с библиотекой FineOffset.h, в ней настраивается количество попыток отправки, по умолчанию - 3, поменял на 2, вероятно приходят данные от первой или второй попытки, отсюда и разница, но она думаю не критична, близка к оригинальным датчикам.
  2. Основная проблема - пропуски отправок, хорошо видно в логе, пока так и не удалось выяснить причин, как варианты - приемник, отсутствие данных от AHTxx, или одно из двух…

Еще раз всем спасибо, как разберусь с подключением по сериал, будет проще выяснить причину пропусков.

Ещё раз скажу: этот код рассчитан на сброс по WDT. Он будет работать только с функцией

Но с этой функцией не возможны никакие циклы.

В вашем случае, код не работает правильно, т.к передаёт одно и то же значение температуры.
#24 должен работать правильно.

Да, 22ой конечно не работает, в погоне за точностью не обратил что AHT в setup.
24ый работает сносно, в пределах 1,5 десятков разбег, но “пропуски” сохраняются, нужно еще поковыряться в библиотеке датчика, или попробовать другую.

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

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

1 лайк

Про эти самые биты и думал, надо пробовать, опыта мало. Но вот сейчас подумалось, раз код из 22 поста отправлял константу а пропуски сохранялись, значит скорее всего проблема не с чтением датчика а с отправкой данных.

Вывод не соответствует логике.

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

Иными словами - если в пятницу закусить три литра водки тазиком несвежего салата и, впоследствии, лежать зелёным все выходные, то никак нельзя обвинять в отравлении исключительно салат.

Попробуйте пожертвовать временем сна. Уменьшите время сна на 1сек(к примеру), и, добавьте это время в условие с timer1 к 1000мс. Так, чтобы перекрыло пропуски.

P.S.
А если дело в отправке, то, можно ещё один таймер добавить, который “перекроет” нестабильное событие.

P.P.S.

Можно ссылку на эту библиотеку? Что-то не пойму , в чём её смысл

Как вариант, по-простому отправку выровнять

Спойлер
#include "lgt_LowPower.h"
#include "FineOffset.h"
#include <Wire.h>
#include <AHTxx.h>
#define TX_PIN 2
#define DEVICE_ID 1235

//AHT20 aht20;
AHTxx aht20(AHTXX_ADDRESS_X38, AHT2x_SENSOR);
FineOffset tx(TX_PIN);
float temp;

void setup() {
  CLKPR = 1<<PMCE;
  CLKPR = 0b00000100;
  Wire.begin();
  aht20.begin();
}

void loop() {
 
  static uint32_t timer1 = 0;
  static bool start = true;
  
 if(start)
  {
  timer1 = millis();
  start = false;
  temp = aht20.readTemperature();// //датчик читаем здесь
  }

  if (millis() - timer1 > 1000 && !start) {
	  
	timer1 = millis();//запоминаем время начала отправки
	
    tx.send(DEVICE_ID, temp);//передаём показания датчика
	
   while(millis() - timer1 < 1000);//ждём, пока пройдёт 1сек 
   //от начала отправки
   
  for (int i=0; i<2; i++) {//время сна придётся уменьшить
    LowPower.powerDown(SLEEP_16S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
  }
  for (int i=0; i<1; i++) {
    LowPower.powerDown(SLEEP_128MS, ADC_OFF, BOD_OFF);
  }
    start = true;
    Serial.println(millis() - timer1);//время от начала отправки
  }
}

По моему уже было в другой теме : написанное выше бессмысленно, т.к. никакого цикла всё равно нет.
Можно просто записать

  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

Я и сам скопировал, не глядя

Да, это проверю.
https://github.com/zagor/FineOffset

Конечно полностью А исключать не стоит)

Попробуйте наоборот, увеличить кол-во отправок.

Глянул, ничего там сложного. Одна отправка данных приёмнику занимает от 12 до 80мс .Но это крайние значения, в реальности длительность где-то между “плавать” будет. Повтор, если задан, через 10мс. Обратной связи нет, просто шлёт crc.
Так что выровнять отправку по времени не мешает.

Другое дело - неизвестно, как среагирует приёмник на несколько отправок подряд - примет за одну, или каждую посчитает. Если за одну - то можно смело добавлять число отправок, чтобы не было пропусков.