Ограничения Arduino и как их обойти?

Тема в разделе "Arduino & Shields", создана пользователем ardurino, 17 дек 2017.

  1. ardurino

    ardurino Нерд

    Здравствуйте. Программирую Arduino UNO и столкнулся вот с чем:
    Подключаю несколько библиотек для DS18B20-термодатчика, GPS, GPRS шилдов. И вроде всё хорошо, но когда пробую отослать POST-запрос, то часть строки теряется. И длина пропавшего куска напрямую зависит от числа подключённых библиотек. Если, например, убрать библиотеки для однопроводной передачи данных для DS18B20-термодатчика - строка полностью передаётся.

    Вопрос: Можно ли эту проблему решить программно?
    Или есть какое-то железо помощнее, другое поколение Arduino? Желательно, чтобы разъёмы были такие же как и у Arduino UNO, чтобы шилды также ставились, в ту жу позицию.

    Код (C++):
    #include "SIM900.h"
    #include <TinyGPS++.h>
    #include <SoftwareSerial.h>
    #include "inetGSM.h"
    InetGSM inet;
    //#include <math.h>
    #include <avr/wdt.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>
     
  2. NikitOS

    NikitOS Король шутов Администратор

    Это весь код?
     
    b707 нравится это.
  3. b707

    b707 Гуру

    Ардурино, когда вы компилируете ваш скетч, акой получается размер кода и используемая память под переменные?
    Для предметного разговора - приведите скетч целиком. Судя по используемым библиотеккам - GSM, GPS и тд - у вас в программе очень много строк - команды модема, интернет-запросы и тд. Эти данные расходуют очень много памяти, особенно если их неправильно использовать.
     
    arkadyf и NikitOS нравится это.
  4. sasha_ml

    sasha_ml Нерд

    Обрабатываю на sim900 смс, в планах подключить беспроводные датчики температуры. Буфер порта Ардуино 64 символа, если программа выполняет какие-то действия и пришло больше 64 символов в порт, куда подключен сим900, все что больше обрежется. Например в библиотеке при опросе датчика DHT22 стоит delay(1000), и если в это время придет строка с модема, то она обрежется. Надо смотреть код основного loop, куда используется время и минимизировать время вызова процедур. Еще можно попробовать скорость порта , по которому идет обмен с модемом понизить.
     
    Последнее редактирование: 22 дек 2017
  5. DIYMan

    DIYMan Guest

    Зачем оно там? Это много - тупить секунду, к тому же незачем при опросе DHT.
     
  6. sasha_ml

    sasha_ml Нерд

    По даташиту ds18b20 (который внутри DHT стоит) так работает, - сначала запрос на чтение данных, пауза 750 мс (датчик внутри себя чего-то там считает), потом само чтение данных. Если контроллер ни для чего другого больше не нужен, то я просто усыпляю его на секунду, а вот если что то-то более сложное (прием смс например, или поступление данных с радио датчиков) нужно что-то мудрить свое с этими паузами. А в библиотеке стандартной просто delay(1000) сделали.
     
  7. AlexU

    AlexU Гуру

    Вижу как минимум два варианта решения:
    1. простой -- переписать библиотеку для датчика под свои нужды;
    2. посложнее -- воспользоваться потоками (http://forum.amperka.ru/threads/Потоки-в-avr.9856/), в этом случае библиотеку править не придётся.
     
  8. ИгорьК

    ИгорьК Гуру

    Ссылочной не поделитесь на даташит где написано что внутри DHT стоит DS18b20?
     
  9. DIYMan

    DIYMan Guest

    С DS18B20 вообще-то не так читают, это раз. Два - в DHT нет DS18B20, насколько я знаю (могу ошибаться, конечно). Три - DS18B20 можно посылать запрос на конвертацию, но температура, рассчитанная датчиком, обновится в РЕГИСТРАХ ДАТЧИКА только через N миллисекунд, зависит от настроенного разрешения датчика. Четыре - библиотека DallasTemperature - кривущая по самые не балуй, в топку.

    Вывод - читать с DS18B20 можно ВСЕГДА и без delay, раз в пару секунд посылая ему запрос на конвертацию. Можете проверить, но только без кривых библиотек: читать с датчика всегда, только раз в несколько секунд просить его сконвертировать новую температуру. Это работает, без всяких ненужных delay.

    Вывод два - извините, но у вас каша в голове.

    Вывод три - с DHT читают не так (это я про delay(1000)). Если вас интересует код чтения с DHT - то он вот такой (кусок из рабочего проекта):

    Код (C++):
    bool CoreSensorDHT::read(uint8_t* buffer)
    {

      uint8_t wakeup_delay = DHT2x_WAKEUP;

      if(sensorType == DHT_11)
        wakeup_delay = DHT11_WAKEUP;

      const uint32_t mstcc = ( F_CPU / 40000 ); // сторож таймаута - 100us

      uint8_t bit = digitalPinToBitMask(pin);
      #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
      uint8_t
      #elif defined (__arm__) && defined (__SAM3X8E__) // Arduino Due compatible
      Pio*
      #else
        #error "Unknown target board!"
      #endif
      port = digitalPinToPort(pin);

      volatile
      #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega328P__)
      uint8_t*
      #elif defined (__arm__) && defined (__SAM3X8E__) // Arduino Due compatible
      RoReg*
      #else
        #error "Unknown target board!"
      #endif
      PIR = portInputRegister(port);

      // начинаем читать с датчика
      pinMode(pin,OUTPUT);
      digitalWrite(pin,LOW); // прижимаем к земле
      delay(wakeup_delay); // и ждём, пока датчик прочухается
      digitalWrite(pin,HIGH); // поднимаем линию
      delayMicroseconds(40); // ждём 40us, как написано в даташите
      pinMode(pin, INPUT_PULLUP); // переводим пин на чтение

      // тут должны проверить последовательность, которую выдал датчик:
      // если линия прижата на 80us, затем поднята на 80us - значит,
      // датчик готов выдавать данные

      uint32_t tmout_guard = mstcc;

      while ((*PIR & bit) == LOW ) // читаем, пока низкий уровень на пине
      {
        if(!--tmout_guard)
         return false; // таймаут поймали
      }
      tmout_guard = mstcc;
      while ((*PIR & bit) != LOW ) // читаем, пока высокий уровень на пине
      {
        if(!--tmout_guard)
         return false; // таймаут поймали
      }

      // считаем, что теперь пойдут данные. нам надо получить 40 бит, т.е. 5 байт.
      uint8_t bytes[5] = {0}; // байты, в которые мы будем принимать данные
      for(uint8_t i=0;i<5;i++)
        bytes[i] = 0;

      uint8_t idx = 0; // индекс текущего байта
      uint8_t bitmask = 0x80; // старший бит байта установлен в единичку, его будем двигать вниз

      for(uint8_t i=0;i<40;i++)
      {
          // сначала ждём 50us, говорящие, что пойдёт следующий бит
          tmout_guard = mstcc;
          while ((*PIR & bit) == LOW )
          {
            if(!--tmout_guard)
                return false; // таймаут поймали
          } // while

          // теперь принимаем бит. Если время подтянутой вверх линии более 40us - это единица, иначе - ноль.

          tmout_guard = mstcc;
          uint32_t tMicros = micros();
          while ((*PIR & bit) != LOW )
          {
            if(!--tmout_guard)
                return false; // таймаут поймали
          } // while

          if(micros() - tMicros > 40) // единичка
          {
            bytes[idx] |= bitmask;
          }

          // сдвигаем маску вправо
          bitmask >>= 1;
       
          if(!bitmask) // дошли до конца байта
          {
            bitmask = 0x80;
            idx++; // читаем в следующий байт
          }
         
      } // for

      pinMode(pin, OUTPUT);
      digitalWrite(pin, HIGH); // поднимаем линию, говоря датчику, что он свободен


      int8_t temperatureValue = 0;
      uint8_t temperatureFract = 0;

      int8_t humidityValue = 0;
      uint8_t humidityFract = 0;


      // проверяем принятые данные
      switch(sensorType)
      {
        case DHT_11:
        {
          uint8_t crc = bytes[0] + bytes[2];
          if(crc != bytes[4]) // чексумма не сошлась
            return false;

         // сохраняем данные
         temperatureValue = bytes[2]; // температура
         humidityValue = bytes[0]; // влажность
       
        }
        break;

        case DHT_2x:
        {
          uint8_t crc = bytes[0] + bytes[1] + bytes[2] + bytes[3];
          if(crc != bytes[4]) // чексумма не сошлась
            return false;

         // сохраняем данные
          unsigned long rh = ((bytes[0] << 8) + bytes[1])*10;
          // влажность
          humidityValue = rh/100;
          humidityFract = rh%100;

         long temp = (((bytes[2] & 0x7F) << 8) + bytes[3])*10;
       
          temperatureValue =  temp/100;
          if(bytes[2] & 0x80) // температура ниже нуля
            temperatureValue = -temperatureValue;
         
          temperatureFract = temp%100;
             
        }
        break;
      } // switch
      if(humidityValue < 0 || humidityValue > 100)
      {
        return false;
      }

      if(temperatureValue < -40 || temperatureValue > 80)
      {
        return false;
      }

      buffer[0] = temperatureValue;
      buffer[1] = temperatureFract;

      buffer[2] = humidityValue;
      buffer[3] = humidityFract;

      return true;
    }
     
    Последнее редактирование модератором: 25 дек 2017
  10. sasha_ml

    sasha_ml Нерд

    http://proline.biz.ua/humidity-and-temperature-sensor-dht22-AM2303
    Возможно dht22 разные бывают, с разными чипами
    http://avrobot.ru/product_info.php?products_id=3855
    здесь говорят. что dht22 это все таки другой датчик с am2302.
    То что датчики можно читать без delay я прекрасно знаю, я то писал о том, что в библиотеках как раз этот delay может стоять, по крайней мере из библиотеки dht его мне пришлось убрать, чтобы не занимать процессор целую секунду, потому как вопрос темы был о том почему теряются данные и предположил что в подключенных библиотеках могут имется задержки, которые и явяются причиной потерь.
     
  11. ИгорьК

    ИгорьК Гуру

    Читайте даташиты, а не бред. Даташит - это документация от производителя.
    Неужели Вы до сих пор не знаете, что в Интернете один производитель и сто безумных писателей.
     
    b707 нравится это.
  12. ardurino

    ardurino Нерд

    Спасибо за советы - выкрутился, найдя более простой код.
    Код (C++):
    #include <OneWire.h>
    //#include <DallasTemperature.h>

    #define POWER_MODE  0 // режим питания, 0 - внешнее, 1 - паразитное
    OneWire sensDs (4);  // датчик подключен к выводу 4

    {
       sensDs.reset();  // сброс шины
      sensDs.write(0xCC, POWER_MODE); // пропуск ROM
      sensDs.write(0x44, POWER_MODE); // инициализация измерения
      delay(900);  // пауза 0,9 сек
      sensDs.reset();  // сброс шины
      sensDs.write(0xCC, POWER_MODE); // пропуск ROM
      sensDs.write(0xBE, POWER_MODE); // команда чтения памяти датчика
      sensDs.read_bytes(bufData, 9);  // чтение памяти датчика, 9 байтов

      if ( OneWire::crc8(bufData, 8) == bufData[8] ) {  // проверка CRC
        // данные правильные
        temperature=  (float)((int)bufData[0] | (((int)bufData[1]) << 8)) * 0.0625 + 0.03125;
     
        // вывод измеренной температуры на индикаторы
                   
           
        // передача температуры на компьютер
        Serial.println(temperature);  
      }
     
  13. b707

    b707 Гуру

    плохой код, опять в середине задержка 0.9сек.... пришли к тому же, с чего начали. Почитайте ответы выше еще раз - вам же говорили, что температуру с датчика ТАК читать неправильно....
     
  14. ardurino

    ardurino Нерд

    Код (C++):
    #include "SIM900.h"
    #include <TinyGPS++.h>
    #include <SoftwareSerial.h>
    #include "inetGSM.h"
    InetGSM inet;
    //#include <math.h>
    #include <avr/wdt.h>
    #include <OneWire.h>
    //#include <DallasTemperature.h>

    #define POWER_MODE  0 // режим питания, 0 - внешнее, 1 - паразитное
    OneWire sensDs (4);  // датчик подключен к выводу 14
    /*
       This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
       It requires the use of SoftwareSerial, and assumes that you have a
       4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
    */

    byte bufData[9];
    float temperature;
    static const int RXPin = 2, TXPin = 3;
    static const uint32_t GPSBaud = 9600;
    //double gpslat;
    String double2string(double n, int ndec){
        String r = "";
        int v = n;
        r += v;     // whole number part
        r += '.';   // decimal point
        int i;
        for (i=0;i<ndec;i++) {
            // iterate through each decimal digit for 0..ndec
            n -= v;
            n *= 10;
            v = n;
            r += v;
        }
        return r;
    };
    double temp, tempC;
    String ltd, lnd,t;
    char msg[50];
    int numdata;
    //char inSerial[50];
    int i=0;
    boolean started=false;
    volatile long cntr;

    // The TinyGPS++ object
    TinyGPSPlus gps;

    // The serial connection to the GPS device
    SoftwareSerial ss(RXPin, TXPin);


    void setup()
    {
     
    Serial.begin(9600);
      ss.begin(GPSBaud);

      Serial.println(F("GPS Strated..."));
      Serial.println();
      TCCR2A = 0;
      TCCR2B = 2;
      TCNT2=59;
      TIMSK2 |= (1 << TOIE2);

    }

    void reboot()
    {
      wdt_disable();
      wdt_enable(WDTO_15MS);
      while (1) {}
    }


    /* double Getterm(int RawADC) {
      temp = log(((10240000/RawADC) - 10000));
      temp = 1 / (0.001129148 + (0.000234125 * temp) + (0.0000000876741 * temp * temp * temp));
      temp = temp - 273.15;
      return temp;
    }

    */


    void displayInfo()
    {
       sensDs.reset();  // сброс шины
      sensDs.write(0xCC, POWER_MODE); // пропуск ROM
      sensDs.write(0x44, POWER_MODE); // инициализация измерения
      delay(900);  // пауза 0,9 сек
      sensDs.reset();  // сброс шины
      sensDs.write(0xCC, POWER_MODE); // пропуск ROM
      sensDs.write(0xBE, POWER_MODE); // команда чтения памяти датчика
      sensDs.read_bytes(bufData, 9);  // чтение памяти датчика, 9 байтов

      if ( OneWire::crc8(bufData, 8) == bufData[8] ) {  // проверка CRC
        // данные правильные
        temperature=  (float)((int)bufData[0] | (((int)bufData[1]) << 8)) * 0.0625 + 0.03125;
     
        // вывод измеренной температуры на индикаторы
                   
           
        // передача температуры на компьютер
        Serial.println(temperature);  
      }
       
     
      Serial.print(F("Location: "));
      if (gps.location.isValid())
      {
      //  sensors.requestTemperatures();
        ltd = (double2string (gps.location.lat(), 6));
        lnd = (double2string (gps.location.lng(), 6));
      //tempC = (sensors.getTempCByIndex(0));
    //  Serial.print(tempC);
       
        t = (double2string (temperature, 1));
     
         Serial.println(ltd);
         Serial.println(lnd);
       
    //  ltd = (double2string (44.391922, 5));
    // lnd = (double2string (33.794126, 5));
      //Read for new byte on serial hardware,
      //and write them on NewSoftSerial.
    // serialhwread();
      //Read for new byte on NewSoftSerial.
    // serialswread();
    InetGSM inet;
      Serial.println("GSM Shield testing.");

      //Start configuration of shield with baudrate.
      //For http uses is raccomanded to use 4800 or slower.
      if (gsm.begin(2400)){
        Serial.println("\nstatus=READY");
        started=true;
      }
      else
      {Serial.println("RESTart GSM+DisInfo");
        digitalWrite(9, HIGH);
          pinMode(9, OUTPUT);

      digitalWrite(9,LOW);

      delay(1000);

      digitalWrite(9,HIGH);

      delay(2000);

      digitalWrite(9,LOW);

      delay(3000);

        displayInfo();
        }
      delay(3000);
      Serial.println("\nstatus=IDLE");
      if(started){
        //GPRS attach, put in order APN, username and password.
        //If no needed auth let them blank.
        if (inet.attachGPRS("internet.mts.ru", "mts", "mts"))
          Serial.println("status=ATTACHED");
        else
          Serial.println("status=ERROR");
        delay(1000);
        //Read IP address.
        gsm.SimpleWriteln("AT+CIFSR");
        delay(5000);
        //Read until serial buffer is emapty.
       gsm.WhileSimpleRead();


    String str = "{\"n\":\"1\", \"t\": \"";
      str += t;
      str += "\", \"d\":\"";
      str += ltd;
      str += "\",\"g\":\"";
      str += lnd;
      str += "\", \"i\":\"2";
      //str += 28;
      str += "\"}";
     
       Serial.println(str);

      //int len = str.length()+1;
      //unsigned char* buf = new unsigned char[len];`

    // str.getBytes(buf, len);
    // Serial.println((const char*)buf);
    numdata=inet.httpPOST("site.com", 80, "/temperature ", str.c_str(), msg, 50);
    //delete buf;

        //TCP Client GET, send a GET request to the server and
        //save the reply.
    //numdata=inet.httpPOST("site.com", 80, "/temperature ", "{\"n\":\"2\", \"t\": \"22\", \"d\":\"44.752095\",\"g\":\"34.554844\", \"i\":\"8\"}",msg,50);
        //Print the results.
        Serial.println("\nNumber :");
        Serial.println(numdata);
        Serial.println("\nData :");
        Serial.println(msg);
     
        Serial.begin(9600);
      ss.begin(GPSBaud);

      }
       
       
      }
      else
      {
        Serial.print(F("INVALID"));
      }

      Serial.print(F("  Date/Time: "));
      if (gps.date.isValid())
      {
        Serial.print(gps.date.month());
        Serial.print(F("/"));
        Serial.print(gps.date.day());
        Serial.print(F("/"));
        Serial.print(gps.date.year());
      }
      else
      {
        Serial.print(F("INVALID"));
      }

      Serial.print(F(" "));
      if (gps.time.isValid())
      {
        if (gps.time.hour() < 10) Serial.print(F("0"));
        Serial.print(gps.time.hour());
        Serial.print(F(":"));
        if (gps.time.minute() < 10) Serial.print(F("0"));
        Serial.print(gps.time.minute());
        Serial.print(F(":"));
        if (gps.time.second() < 10) Serial.print(F("0"));
        Serial.print(gps.time.second());
        Serial.print(F("."));
        if (gps.time.centisecond() < 10) Serial.print(F("0"));
        Serial.print(gps.time.centisecond());
          Serial.flush();
      }
      else
      {
        Serial.print(F("INVALID"));
      }


    }
    void loop()
    {
      // This sketch displays information every time a new sentence is correctly encoded.
      while (ss.available() > 0)
      {
        if (gps.encode(ss.read()))
        {
    //    double temp = Getterm(analogRead(4));
          displayInfo();
        }
      }
      if (millis() > 5000 && gps.charsProcessed() < 10)
      {
        Serial.println(F("No GPS detected: check wiring."));
        while(true);
      }
    }


    ISR(TIMER2_OVF_vect) {
      TCNT2=59;//55;
      cntr++;
      if(cntr>2511111)
      {
        cntr = 0;
        Serial.println(F("Q_Reset"));
       // delay (1000);
       // resetFunc();
        reboot();

      }
    }



     
     
  15. ardurino

    ardurino Нерд

    Не оптимизированный код, настолько, что если в коде добавить просто одну переменную типа String, уже строка POST-запроса обрезается.
    Код (C++):
    10.108.111.134
    {"n":"1", "t": "19.9", "d":"55.697425","g":"37.569889"}
    POST /temperature  HTTP/1.1
    Content-Type: application/json
    X-CSRF-Token:
    Host: site.com
    Content-Length: 55

    {"n":"1", "t": "19.9", "d":"55.697425","g":"37.569889"}
    Хотя должны быть полная строка такая:
    Код (C++):
    {"n":"1", "t": "35.0", "d":"55.697338","g":"37.569671", "i":"2"}
    А хотелось бы ещё параметров добавить в отправляемый POST-запрос.
     
  16. b707

    b707 Гуру

    Кто мешает отослать два запроса?
     
    NikitOS нравится это.
  17. ardurino

    ardurino Нерд

    Вот эта строчка всё содержит, что нужно,
    Код (C++):
    {"n":"1", "t": "35.0", "d":"55.697338","g":"37.569671", "i":"2"}
    мне осталось лишь чтобы, например 'i":25" влезало. А сервер дургой запрос ней поймёт. Либо надо сервер переделывать.
     
  18. ardurino

    ardurino Нерд

    Попробую методом тыка поурезать, то, что не будет влиять. Может убрать некоторые информационные сообщения. Главное, чтобы машина отправляла, пускай без наглядности.
     
  19. b707

    b707 Гуру

    String-и передалайте в char, особенно вашу главную str. Зачем вы ее в виде String создаете, а потом преобразуете c_str() - так сразу в виде c_str и создавайте, выиграете, как минимум столько же байт, сколько вся строка занимает.
    Кроме этого, у вас в начале программы выделяется буфер msg[50] длиной 50 байт - не нашел, чтобы он где-то использовался. Ну и даже если он в каком-то месте программы нужен - зачем его делать глобальным? Сделайте локальным в процедуре - выиграете еще 50 байт...
    Ну и вообще, код очень непричесанный. тут обьем памяти, ИМХО, на сотни байт можно сократить...
     
    Securbond нравится это.
  20. ardurino

    ardurino Нерд

    С этим даже справился...
    А с этим пока не получается. Там есть функция, которая преобразует double в строку с нужной точностью после запятой. Просто заменой String на char не получается. И на этой функции многое держится, похоже.
    Код (C++):
     String double2string(double n, int ndec){
        String r = "";
        int v = n;
        r += v;     // whole number part
        r += '.';   // decimal point
        int i;
        for (i=0;i<ndec;i++) {
            // iterate through each decimal digit for 0..ndec
            n -= v;
            n *= 10;
            v = n;
            r += v;
        }
        return r;
    }