HC-SR04 безбожно врёт

Viktor1306
Viktor1306 аватар
Offline
Зарегистрирован: 19.05.2016

Помогите решить проблему. Требуется создать GPS трекер для грузовых контейнеров, который помимо координат должен передавать наполненность контейнера, вскрытие и пожар. Основное требование -  устройство должно работать без зарядки до 6 месяцев. В качестве датчика наполнения я выбрал ультразвуковой сонар HC-SR04. Скетч, написанный мной в начале лета всех устраивал, но сейчас, то ли из за холода, то ли просто время пришло, короче начали массово садиться аккумуляторы. Было решено переделать девайсы. В старом использовались 12 V аккумуляторы + КРЕН 5 (КР142ЕН5) который, как выяснилось сам потребляет энергию, в новом же я решил использовать Li-Pol аккумулятор на 3,7v. Соответственно Ардуину я взял Arduino pro mini на 3,3v HC-SR04 запитал через DC/DC преобразователь, так как эти модули отказываются работать от 3,3 v. Всё прекрасно работало, пока я не решил загонять МК в сон. Скачал на этом форуме пример, поправил его, и тут выяснилась приниприятная вещь. По отдельности скетчи работают нормально (один для сонара, другой для сна), а вот вместе, ну ни как не хотят. Сонар ужасно врёт (показывает расстояние от 6 см до реального, но каждый раз разное), а тот что отвечает за сон, виснет, при чём в тот момент, когда он всё узнал и должен отправить данные на сервер. Хотя на данный момент он ни чего не отправляет, а вместо отправки на сервер, отправляет соответствующее сообщение в консоль.

Вот злосчастный скетч.

#include <avr/sleep.h>
#include <avr/wdt.h>
#include <SoftwareSerial.h>
int wakePin = 2;                 // Пин используемый для просыпания (прерывания)
int sleepStatus = 0;             // Переменная для хранения статуса (спим, проснулись)
int LedPin=13;                   // Светодиод, включение обвязки
int count;                       // Счётчик пробуждений
const int slp = 3;               // Количество пробуждений до включения сонара
bool wdog, alarm;                // Сработал таймер, тревога

#include <Ultrasonic.h>
Ultrasonic ultrasonic(10,11); //trig, echo
int ds = 0;                   //Здесь хранится расстояние для сравнения 
bool snr = false;

#include <TinyGPS.h>
TinyGPS gps;
SoftwareSerial ss(4, 3); //RX, TX
String lat, lon;         //Здесь хранятся широта и долгота для сравнения
bool gpson;

ISR (WDT_vect) 
{
 wdog = true; 
 wakeUpNow(); // пробуждение по таймеру
} 

void wakeUpNow()        // Прерывание сработает после пробуждения
{
  if (sleepStatus)               // Если мы спали,
  {
    sleep_disable();             // то первое, что нужно сделать после просыпания - выключить спящий режим
    wdt_disable();               // загоняем собаку в конуру
    sleepStatus = 0;             // В переменную заносим статус бодрствования
      if(wdog == false){         // если не по таймеру, значит тревога
      alarm = true;
      }
  }
}
void setup()
{
  //сон - пробуждение
  count = 0;
  pinMode(LedPin, OUTPUT);
  pinMode(wakePin, INPUT);
  digitalWrite(wakePin, HIGH);               // Подтягивем ногу к 5.
  digitalWrite(LedPin, LOW);
  delay(5000);       // на всякий случай, если войдет в бесконечный цикл, можно будет перепрошить
  //инициализация портов
  Serial.begin(115200); // удалить после настройки
  //предварительные установки
  gpson = false;
  wdog = false;
  alarm = false;
  Serial.println("INIT");
}

void sleepNow()         // Функция увода ардуины в спячку.
{ 
  delay(5000); 
  // подготовка WatchDog
  wdog = false;  
  MCUSR = 0;  // clear various "reset" flags
  WDTCSR = bit (WDCE) | bit (WDE); // allow changes, disable reset
  // set interrupt mode and an interval   
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  //WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);  // set WDIE, and 1 second delay
  wdt_reset();  // pat the dog
  // конец подготовки WatchDog
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // Здесь устанавливается режим сна
  sleep_enable();                        // Включаем sleep-бит в регистре mcucr. Теперь возможен слип
  attachInterrupt(0, wakeUpNow, FALLING); // Используем прерывание 0 (pin 2) для выполнения функции wakeUpNow (прерывание вызывается только при смене значения на порту с HIGH на LOW - подтянуть ногу 2 на 5в.)                           
  sleepStatus = 1;                       // В переменную заносим статус сна
  sleep_mode();                          // Здесь устройство перейдет в режим сна!!!                           
  sleep_disable();                       // Первое, что нужно сделать после просыпания - выключить спящий режим
  detachInterrupt(0);                    // Выключаем прерывание - при нормальном режиме wakeUpNow() не будет вызываться
}

void transmit(int d, String l){
  if(d == 1){ 
   Serial.println("TRANSMIT "+l); 
   }
digitalWrite(LedPin, HIGH);  // Выключаем светодиод 
delay(500);
snr = false;
Serial.println("SLEEP");
sleepNow();     // Вызов функции sleep() для засыпания  
}

String sonar(){ // функция определения расстояния
 int us = 0; 
 delay(5000);
 for (int i = 0; i < 10; i++){
  us = us + ultrasonic.Ranging(CM);
  Serial.println(ultrasonic.Ranging(CM));
  delay(1000);
  Serial.println("OK");
 }
 us = us/10;
 if(ds != us){
 Serial.println("TR");
 transmit(1,String(us));
 ds = us;
 } else {
  Serial.println("ITOGO "+String(us));
  Serial.println("SLEEP");
  snr = false;
  sleepNow();
 }
}

void get_gps(){ // получаем координаты
  ss.begin(9600);
  bool newData = false;
  unsigned long chars;
  unsigned short sentences, failed;
  String locla, loclo;
  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (ss.available())
    {
      char c = ss.read();
     // Serial.write(c); // uncomment this line if you want to see the GPS data flowing
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;
    }
  }

  if (newData)
  { 
    float flat, flon;
    unsigned long age;  
    gps.f_get_position(&flat, &flon, &age);
    locla = String(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);
    loclo = String(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);
    Serial.println("LAT= "+locla+" LON= "+loclo); // удалить после настройки
    if(gpson == false){
    gpson = true;  
    transmit(0,"917"); // инициализация выполнена
    } else {
      if((lat != locla)or(lon != loclo)){
       transmit(0,"902"); // тревога 
       lat = locla;
       lon = loclo;
      }
    }
  }
}

void loop()
{
  if(alarm == true){
    Serial.println("ALARM");
    digitalWrite(LedPin, LOW);
    delay(5000);
    get_gps();
    alarm = false;
  }
  
  if(gpson == false){
    delay(5000);     
    get_gps();
  } else {
   
 if(snr == false){ 
  if(count < slp){
    count++;
    sleepNow();
    } else {
      snr = true;
      count = 0;
      Serial.println("WAKE");
      digitalWrite(LedPin, LOW);
      sonar(); //включаем сонар 
      }
 
   }
  }
      
}

А вот схема

Viktor1306
Viktor1306 аватар
Offline
Зарегистрирован: 19.05.2016

С зависанием вроде разобрался, у меня в функции sonar вместо void стояло String. Наверно осталось от старой, где она возвращала расстояние. А вот глюк с расстояниями так и остался...

А вот что выводится в консоль

INIT
LAT= 54.347668 LON= 48.335850
SLEEP
WAKE
8
OK
23
OK
6
OK
23
OK
23
OK
23
OK
23
OK
16
OK
23
OK
23
OK
TR
TRANSMIT 20
SLEEP
WAKE
7
OK
9
OK
6
OK
23
OK
23
OK
23
OK
23
OK
23
OK
23
OK
23
OK
TR
TRANSMIT 18
SLEEP
WAKE
7
OK
23
OK
23
OK
23
OK
23
OK
23
OK
23
OK
23
OK
22
OK
23
OK
TR
TRANSMIT 21
SLEEP
WAKE
7
OK
23
OK
23
OK
23
OK
23
OK
15
OK
23
OK
23
OK
23
OK
23
OK
ITOGO 21
SLEEP
WAKE
6
OK
6
OK
6
OK
6
OK
9
OK
9
OK
6
OK
9
OK
6
OK
6
OK
TR
TRANSMIT 2
SLEEP
WAKE
3
OK
6
OK
7
OK
9
OK
12
OK
12
OK
6
OK
6
OK
6
OK
6
OK
ITOGO 2
SLEEP
WAKE
7
OK
9
OK
24
OK
6
OK
6
OK
3
OK
6
OK
8
OK
34
OK
6
OK
ITOGO 2
SLEEP
 
XOR
Offline
Зарегистрирован: 25.04.2015

есть такое дело, сам наступил на эти грабли, после подачи питания на HC-SR04 нужно выждать около 1 секунды, сделать замер, игнорировать его, выждать 100мс и произвести новый замер, он будет валидный.

Viktor1306
Viktor1306 аватар
Offline
Зарегистрирован: 19.05.2016

Спасибо за информацию, так оно и есть. Даже 10 замеров с вычислением среднего арифметического делать не нужно. Только интервалы колеблются для каждого из сонаров. Для одного 100мс нормально, а для другого 400 ставить пришлось. 

XOR
Offline
Зарегистрирован: 25.04.2015

кстати про враньё, у меня он лежит на полке и смотрит в крышу, слева дистанция в сантиметрах http://savepic.net/8531887.htm