Зависание Arduino

xorkrus
Offline
Зарегистрирован: 22.09.2013
#include <SPI.h>
#include <Wire.h>
#include "DHT.h"
#include "Adafruit_BMP085.h"
//#include <ETHER_28J60.h>
#include <EtherShield.h>

#define DHT_S1_PIN A0    // пин для датчика DHT22
int humidity = 0, temp_dht = 0, temp_bmp = 0, temp = 0;
int32_t pressure_pa = 0, pressure_mm = 0;
int mode = 0;
dht dht_s1;
Adafruit_BMP085 bmp;
static uint8_t mymac[6] = {
  0x54,0x55,0x55,0x10,0x00,0x25}; 
  
static uint8_t myip[4] = {
  192,168,1,88};

#define MYWWWPORT 80
#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];

// The ethernet shield
EtherShield es=EtherShield();

uint16_t http200ok(void)
{
  return(es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n")));
}
int add_string(char* str, uint16_t &plen) {
  int i = 0;
  
  //Loop through each char
  while (str[i]) {
    // Add each char one by one to the buffer
    buf[TCP_CHECKSUM_L_P + 3 + plen] = str[i];
    i++;
    plen++;
  }
}
// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf)
{
  Z1_sensors_update();
  Z1_SerialOutput();
  uint16_t plen;
  plen=http200ok();
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<html><head><title>Weather Station</title></head><body>"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><h2><font color=\"blue\">-- ARDUINO OnLine -- </font></h2></center><hr>"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><table border=1 align=center>"));
  
  char mystr[400];
  sprintf(mystr,"<tr><td>&nbsp;&nbsp;Humidity DHT&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d %%&nbsp;&nbsp;</td></tr>",humidity);
  add_string(mystr, plen);
  sprintf(mystr,"<tr><td>&nbsp;&nbsp;Pressure BMP&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d mm&nbsp;&nbsp;</td></tr>",pressure_mm);
  add_string(mystr, plen);
  sprintf(mystr,"<tr><td>&nbsp;&nbsp;Temp DHT&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d *C&nbsp;&nbsp;</td></tr>",temp_dht);
  add_string(mystr, plen);
  sprintf(mystr,"<tr><td>&nbsp;&nbsp;Temp BMP&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d *C&nbsp;&nbsp;</td></tr>",temp_bmp);
  add_string(mystr, plen);
  
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</table></center></body></html>"));
  return(plen);
}

void setup(){
  SPI.begin();
    // Initialise SPI interface
  es.ES_enc28j60SpiInit();

  // initialize enc28j60
  es.ES_enc28j60Init(mymac);

  // init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip, MYWWWPORT);
  if (!bmp.begin()) {
    Serial.println("ERROR: BMP085 sensor failed");
  }
  Serial.begin(9600); 
  Serial.println("\n ...---===[ Weather Station ]===---...\n"); // Тестовые строки для отображения в мониторе порта
}

void loop(){
  char buff[64];
  int len = 64;
  mode = (mode + 1) % 100;

  uint16_t plen, dat_p;
  while(1) {
    // read packet, handle ping and wait for a tcp packet:
    dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf));

    if(dat_p==0){
      // no http request
      continue;
    }
    // tcp port 80 begin
    if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
      // head, post and other methods:
      dat_p=http200ok();
      dat_p=es.ES_fill_tcp_data_p(buf,dat_p,PSTR("<h1>200 OK</h1>"));
      goto SENDTCP;
    }
    // just one web page in the "root directory" of the web server
    if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
      dat_p=print_webpage(buf);
      goto SENDTCP;
    }
    else{
      dat_p=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>401 Unauthorized</h1>"));
      goto SENDTCP;
    }
SENDTCP:
    es.ES_www_server_reply(buf,dat_p); // send web page data
    // tcp port 80 end
  
  }
}
void Z1_sensors_update() {
// if (mode%30==0) {
   // BMP085 (Temp and Pressure)
  temp_bmp = bmp.readTemperature();
  pressure_pa = bmp.readPressure();
  pressure_mm = pressure_pa/133.3;
  // DHT22 (Temp)
  if (dht_s1.read22(DHT_S1_PIN) == DHTLIB_OK) {
    humidity = dht_s1.humidity;
    temp_dht = dht_s1.temperature;
    temp = temp_dht;
  } else {
    temp = temp_bmp;
  }
// }
}

void Z1_SerialOutput() {
  Serial.print("T1 = "); 
  Serial.print(temp_dht);
  Serial.print(" *C | ");
  Serial.print("T2 = "); 
  Serial.print(temp_bmp);
  Serial.print(" *C | ");
  Serial.print("Pressure = "); 
  Serial.print(pressure_mm);
  Serial.print(" mm | ");
  Serial.print("Humidity = "); 
  Serial.print(humidity);
  Serial.print(" %");
  Serial.print("\n\n");
}

Собственно вот. Дело значит обстоит так:

Включаем arduino и в терминале появится надпись инита

Заходим браузером на 192.168.1.88 и видим табличку нужную. Одновременно в уарт вылетит та же информация, что и на страничку

Нажимаем в браузере F5 и... всё. Ардуина зависает и не откликается ни на какие действия. После ресета ситуация повторяется.

В чем может быть проблема?

toc
Offline
Зарегистрирован: 09.02.2013

1. какая у вас ардуина?
2. если не мега, то, наверняка, у вас проблема с нехваткой оперативки
3. закомментируйте большую часть метода print_webpage и добейтесь стабильной работы.
4. помните, что все браузеры дополнительно отправляют запрос на адрес /favicon.ico

xorkrus
Offline
Зарегистрирован: 22.09.2013

1. pro mini atmega328

2. как посчитать сколько будет заюзано памяти?

3. урезал вывод - помогло. спасибо.

4. а это имеет значение? ардуинка же не отвечает и не обрабатывает такой запрос...

leshak
Offline
Зарегистрирован: 29.09.2011

>3. урезал вывод - помогло. спасибо.

Еще про экономию:

Каждая текстовая строка в коде вида "some string" - забирает у вас оперативки "длина строки+1"

Вот поэтому код вида 

PSTR("<center><h2><font color=\"blue\">-- ARDUINO OnLine -- </font></h2></center><hr>")

Предпочтительнее чем 

 sprintf(mystr,"<tr><td>&nbsp;&nbsp;Humidity DHT&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d %%&nbsp;&nbsp;</td></tr>",humidity);

В первом случае у вас строка "выжрала оперативу", а во втором она осталась во Flash-памяти и не грузанулась автоматом в RAM при старте скетча.

Флеша у вас есть 32 KB (-2kb на бутлоадер), а SRAM 2KB на все про все. Хотя с SRAM и проще работать, но....

А два КБ - это не так уж и много. Как делаете строку длинной 2023 и больше ни на что оперативки вообще не осталось.

Далее:

char mystr[400];

Осторожней с размером массивов. Одной этой строкой вы забрали под этот массив 400 байт из 2048. 20% памяти. И это не считаю того что само использование spintf тоже заставляет компилятор "добавить еще одну библиотеку в итоговый скетч".

А еще, раз у вас Enc28j60, то значит весь TCP/IP стек реализован програмно, а не аппаратно. И сама библиотека не маленькая, и на ее нужды, буфер вы выделили 500 байт.

А еще библиотеки датчиков, а еще буфферок 

char buff[64];

Повезло что оно вообще "взлетело". Прямо как в анекдоте

Первый полет: люди зашли расселись по местам, стюардесса обьявляет:
"Добро пожаловать на наш 5-ти этажный самолет."
"Спасибо, что выбрали именно нашу компанию"
"Итак, на первом этаже находимся мы, вы можете сидеть и наслаждаться
полетом..."
"... На втором этаже находятся бар и казино..."
"... На третьем этаже находятся площадка для гольфа и спорт-комплекс..."
"... На четвертом этаже находится кинотеатр..."
"... На пятом этаже находятся бассейн и аква-парк..."
"... Ну а теперь со всей этой херней мы попробуем взлететь!"

Вообщем если есть возможность - лучше переходите на wiznet (а него аппаратный TCP/IP, и стабильней, и быстрее и меньше ресурсов жрет). Блоаго в цене, спасибо китайцам, они упали. Плюс у него часто уже бывает встроенная SD-карта. Можно на ней хранить "странички, картинки, CSS-ски" (хотя писать на ардуино полноценное WebUI  - тот еще гимор).

Осваивайте PROGMEM и PSTR http://scott.dd.com.au/wiki/Arduino_Static_Strings

Под лупой рассмативайте каждый массив. Думайте можно ли кого-то из них "заюзать два раза". Внимательно смотрите за размерами массивов, типов переменных.

Скажем

int mode = 0;

Судя по ее названию ей бы за глаза хватило и одного байта, вместо двух. Или у вас действительно может быть разных режимов больше 255-ти?

P.S. А если еще вниматеьленй посмотреть, то вообще не очень ясно зачем эта mode там присутсвует.

xorkrus
Offline
Зарегистрирован: 22.09.2013

Обалденно. Вот если бы так отвечали... Спасибо за развернутый ответ.

Изначальный код был нерабочим. Там стер - тут закомментировал - здесь поменял... и остался mode :) 

Этот сетевой модуль брался как самый дешевый для чисто посмотреть. Bтоговый вариант девайса это почти то, что надо, но громоздко - будем уменьшать. SD-карты и прочие модули у меня есть самопаянные. но нне требуются.

sprintf был использован как первый попавшийся вариант передачи в вывод значения переменных.

И этот момент объяснений не совсем понятен: разве оптимизируя использование рам, не логичнее хранить во флеш-памяти (sprintf), нежели грузить в рам (PSTR)?

leshak
Offline
Зарегистрирован: 29.09.2011

sprintf - ничего не хранит. это функция форматирования строки. Причем в RAM.

PSTR - ничего не грузит (скорее даже наоборот: указание "не грузить").

toc
Offline
Зарегистрирован: 09.02.2013

кто-то моё сообщение стёр ~19:10. id=63735.
:(

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

>3. урезал вывод - помогло. спасибо.

Еще про экономию:

Каждая текстовая строка в коде вида "some string" - забирает у вас оперативки "длина строки+1"

Вот поэтому код вида 

PSTR("<center><h2><font color=\"blue\">-- ARDUINO OnLine -- </font></h2></center><hr>")

Предпочтительнее чем 

 sprintf(mystr,"<tr><td>&nbsp;&nbsp;Humidity DHT&nbsp;&nbsp;</td><td>&nbsp;&nbsp;%d %%&nbsp;&nbsp;</td></tr>",humidity);

В первом случае у вас строка "выжрала оперативу", а во втором она осталась во Flash-памяти и не грузанулась автоматом в RAM при старте скетча.

Вот блин, насоветовал. Предпочтительность - правильно. Причина - все с точностью наоборот. PSTR оставила строку во flash. А просто "bla-bla" - выжрала память. Поэтому PSTR - предпочтительней (или PROGMEM если объявляем переменную)  Отсюда и у вас пошла путаница кто хранит в памяти, а кто во флеше.