помогите уменьшить размер скетча

eldev
Offline
Зарегистрирован: 14.05.2012

сделал скетч, читающий показания с датчиков bmp085 и dht11 и отсылающую на определенный адресс в сети, скетч не помещается на 306 байт большая просьба помочь с уменьшением размера

//
// Ethernet shield attached to pins 10, 11, 12, 13
//
// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here
//
// Connect pin 1 (on the left) of the DHT11 sensor to +5V
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
//

#include <SPI.h>
#include <Ethernet.h>
//#include <EthernetUdp.h>
#include <SD.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <DHT.h>
//#include <DallasTemperature.h>
//#include <OneWire.h>

#define DHTPIN 2
#define DHTTYPE DHT11
//#define ONE_WIRE_BUS 10
//#define TEMPERATURE_PRECISION 9

DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP085 bmp;
EthernetClient client;
IPAddress server(192,168,1,1);
//0neWire oneWire(ONE_WIRE_BUS);
//DallasTemperature sensors(&oneWire);
const int chipSelect = 4;

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {  
  0xDE, 0xAD, 0xBE, 0xFF, 0xFE, 0xED }; 
  IPAddress ip(192,168,1,36);

// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
unsigned int localPort = 8888;      // local port to listen for UDP packets

IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 

// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;



void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
  // this check is only needed on the Leonardo:
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // DHCP failed, so use a fixed IP address:
    Ethernet.begin(mac, ip);
  }
//конец инициализации сети

Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

dht.begin();
Udp.begin(localPort);
}

void loop() {
  unsigned long epoch = 0;
  //String data ="";
  int data_pos = 0;
  char data[100] = "";
  
  sendNTPpacket(timeServer); // send an NTP packet to a time server
    // wait to see if a reply is available
  delay(1000);  
    if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    epoch = secsSince1900 - seventyYears;  
    // print Unix time:
    //Serial.println(epoch); 
  }

File dataFile = SD.open("weather.txt", FILE_WRITE);
if (dataFile) {
    dataFile.print(epoch);
    dataFile.print(",");
    dataFile.print(bmp.readTemperature());
    dataFile.print(",");
    dataFile.print(dht.readHumidity());
    dataFile.print(",");
    dataFile.println(bmp.readPressure());
    dataFile.close();
  }  
    if (client.connect(server, 80)) {
    Serial.println("connected");
    dataFile = SD.open("weather.txt");
    if (dataFile) {
    //Serial.println("weather.txt:");
    // read from the file until there's nothing else in it:
    while (dataFile.available()) {
    	//data = char(dataFile.read());
        data[data_pos] = char(dataFile.read());
        data_pos++;    
    }
    data_pos = 0;
    // close the file:
    dataFile.close();
  }
    // Make a HTTP request:
    client.print("POST /getinfo.php?q=");
    client.print(data);
    client.println(" HTTP/1.1");
    client.println();
  } 
  else {
    // kf you didn't get a connection to the server:
    Serial.println("connection failed");
  }
  
}

unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

 

ustas
Offline
Зарегистрирован: 12.03.2012

 имхо, убрать все Serial.print

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Вообще класс Serial убрать. вместе с сообщениями. Если все же нужны сообщения, то их по максимуму сократить, тексты разместить в eeprom (они сейчас прямо в програмной области валяются.

eldev
Offline
Зарегистрирован: 14.05.2012

а можно подробнее про размещение текстов в eeprom? просто скетч еще не до конца закончен...

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

eldev пишет:

а можно подробнее про размещение текстов в eeprom? просто скетч еще не до конца закончен...

Гуглите слова PSTR, PROGMEM, prog_char,pgm_read_byte и т.п. в связке с ардуиной.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

leshak пишет:

eldev пишет:

а можно подробнее про размещение текстов в eeprom? просто скетч еще не до конца закончен...

Гуглите слова PSTR, PROGMEM, prog_char,pgm_read_byte и т.п. в связке с ардуиной.

Человеку нужно в eeprom, а не в progmem

Вот только уж больно ущербная работа с епромом в ардуино. Я попробова, в принципе, может, вот только IDE почему-то не пишет eeprom :(

Или нужно сначала написать скетч, который зальет в еепром все, что надо, чтобы потом читать в основной программе, или воспользоваться программатором. Есть еще вариант "через дудку" - здесь описано у меня прочитать получилось не с первого раза. Вот такая строка прочитала:

avrdude -q -C ..\etc\avrdude.conf -p m328p -c arduino -P COM5 -b 115200 -U eeprom:r:0.bin:r

Но, содержимое не очень похоже на правду... Надо будет при случае программатором проверить.

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

 

AlexFisher пишет:

Человеку нужно в eeprom, а не в progmem

Да... на автомате ответил. Просто еще рядом еще пара подобных веток одновременно шла. Но тут еще возникает вопрос, а нужно ли человеку eeprom?  У него скетч во флеш не влазит или оперативки не хватает? Если второе - то IMHO есть шанс отделатся "малой кровью". Просто не грузить строки в оперативку, не заморачиваясь с eeprom. 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Человек ясно сказал - 306 байт у скетча лишние - то есть прога во флеш не лезет. До оперативки еще дело дойдет :) потом...

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

 

AlexFisher пишет:

Человек ясно сказал - 306 байт у скетча лишние - то есть прога во флеш не лезет. До оперативки еще дело дойдет :) потом...

Ну проглядел, простите. Но как говорил Хаус - все врут ;)

 

2eldev:

  • http://arduino.cc/en/Tutorial/Memory - вообщам-то стартовая точкя для чтения "про память и размеры"
  • http://www.arduino.cc/en/Reference/PROGMEM - на PROGMEM дока.
  • http://www.arduino.cc/en/Reference/EEPROM - дока на стандартную либу. Но она умеет читать/писать только по одному байту. Так "все остальное дело ваших рук" (вычитывать по байту, и складывать куда-то в буфер или отправлять по инету ;)
  • http://arduino.cc/playground/Code/EepromUtil - всякие хелперы для более удобного чтения/сохранения строк и int-тов в EEPROM
  • http://www.arduino.cc/playground/Code/EEPROMWriteAnything - еще один хелпер для чтения/записывания любого объекта (структуры там сохранить/прочитать или массив целиком)
  • Но 100% уверенности что все это поможет - нет.

В конце концов чудес не бывает и всегда можно наподключать либ столько что они не влезут в кристал никакими ухищрениями и выходом будет переход на более жирный кристал (или отказ от какой-то функциональности). Возможно вы уменьшите размер скетча за счет строк, но при этом увеличите за счет библиотек для EEPROM :(
Но тут "только пробовать" :(

Попробуте, для начала, как советовали выше, тупо повыкидвать все Serial.print которые можно. В тех которые "нельзя выкинуть никак" - сделать строки более короткими (какие-то абревиатуры использовать вместо целих слов, сокращения и т.п.)

 

eldev
Offline
Зарегистрирован: 14.05.2012

leshak пишет:

Попробуте, для начала, как советовали выше, тупо повыкидвать все Serial.print которые можно. В тех которые "нельзя выкинуть никак" - сделать строки более короткими (какие-то абревиатуры использовать вместо целих слов, сокращения и т.п.)

serial.print'ы все поубирал - помогло, вот только скетч еще не дописан. нужно еще добавить удаление файла и задержку минут в 5. (добавил удаление файла - размер скетча опять выше нормы) 

у меня вопрос именно в том что скетч по своим размерам не проходит в 30кБ

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

 >(добавил удаление файла - размер скетча опять выше нормы)

Ну чудес не бывает. Если все подключеные вами библиотеки практически не оставляют места для вашего скетча - что ж с этим поделаешь? .zip -па тут нет :(

Варианты:

  1. Брать более жирную ардуну/камень (IMHO проще всего. да и учитывая "стоимость времени" - дешевле всего)
  2. Отказывать от ArduinoIDE, тянущихся за ней библиотек и переходить на "голый C" или ассемблер (тогда точно влезет :) )
  3. Лезть в кишки библиотек и пытатся их оптимизировать
  4. Отказыватся от библиотек, попытатся "выдрать из них куски" которые вы используете и похерить то что "вам не нужно". Вообщем попытатся "работать на прямую", без библиотеки в надежде что у вас это получится более эффективно чем у автора библиотеки. А на библиотеку смотреть просто как на "справочный пример".
leshak
Offline
Зарегистрирован: 29.09.2011

 Ну еще, как вариант, забить на получение времени от TimeServer. А в место него купить микруху часов RTS. И надеяется что работа с ней будет занимать меньше памяти чем работа с сервером. Хотя, конечно, будет и не так точно на больших промежутках времени (но это уже зависит от того насколько точную микруху возьмете). Плюсом будет еще и то, что дуина сможет работать и запоминать погоду даже в отсуствие интернета. Тупо писать лог на карточку. И знать при этом время :)

eldev
Offline
Зарегистрирован: 14.05.2012

да думал я об rts изначально, купил соответственно микросхему ds1307 с обвязкой встал вопрос об изготовлении платы, так что с ntp решение планировалось как бы временное... и еще хотелось использовать milis() вместо delay() с соответствующими изменениями...

теперь с новыми реалиями надо будет проверить целесообразность rts 

как вариант убрать библиотеки bmp085 и dht и работать напрямую c i2c и 1-wire (dht заменить на sht11 тогда вообще 1-wire не нужна будет)

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

eldev пишет:

да думал я об rts изначально, купил соответственно микросхему ds1307 с обвязкой встал вопрос об изготовлении платы,

Можно и изготовить - опыт пригодится

А можно и купить готовую. Типа вот такой http://we.easyelectronics.ru/HomeTech/tqfp32---dip32-perehodnik-dlya-maketirovaniya.html

Видел на рынке такие под разные корпуса (только без прижима сверху, "под пайку" заточенные).

Либо панельку купить (но фиг найдешь и очень дорого).

Либо что-то такое намонстрячить

eldev пишет:

так что с ntp решение планировалось как бы временное...

Можно, в качестве "временного" - по сериал вермя читать. а на компе какую-то програмульку состряпать которая будет время сообщать.

В любом случае попробуйте выкинуть этот timeServer и посмотрите насколько уменьшится скетч (может быть и не уменьшится, может он сильно с клиентом "переплетен").

Потом можете попробовать добавить  RTC либу и немного работы с ней (плевать, даже если микрухи нет - все равно размер скетча увидите).

Вообщем прикинте поможет вам переход на микруху или это "те же яйца только в профиль".

Цитата:

и еще хотелось использовать milis() вместо delay() с соответствующими изменениями...

http://arduino.ru/tutorials/BlinkWithoutDelay  (и по форуму задачат типа "включить что-то на какой-то время без delay - масса была).

Цитата:

как вариант убрать библиотеки bmp085 и dht и работать напрямую c i2c и 1-wire

Ну "для работы" все равно код писать нужно будет. И не факт что он выйдет меньше чем у тех кто библиотеку писал :(

eldev
Offline
Зарегистрирован: 14.05.2012

Да, именно про этот пример "мигание без delay()" я и имел ввиду, говоря про millis()

вопрос только в том что не на комп будет слаться информация, а на роутер zyxel keenetic с установленным на него lighttpd c php и sqlite, поэтому и идет запись на карту памяти с временными метками на случай отваливания сервера (после успешной передачи информации файл должен обнуляться)

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

eldev пишет:

Да, именно про этот пример "мигание без delay()" я и имел ввиду, говоря про millis()

Ну дык в чем проблема? :)

eldev пишет:

вопрос только в том что не на комп будет слаться информация, а на роутер zyxel keenetic с установленным на него lighttpd c php и sqlite,

А какая разница куда оно шлется? В том и суть http что про это можно не знать.

eldev пишет:

поэтому и идет запись на карту памяти с временными метками на случай отваливания сервера (после успешной передачи информации файл должен обнуляться)

А зачем вам тогда вообще, на стороне адруины знать точное время? Пишите в файл временные метки от millis(). И посылайте millis(). А уж когда запихиваете в базу - добавляйте текущие время роутера. Он-то время должен знать у большинства синхронизация времени "от рождения" имеется. А если нет - так запрос к timeServer на роутере сделать.

eldev
Offline
Зарегистрирован: 14.05.2012

можно ли средствами самой ардуины обнулять millis()?

ардуину планирую связывать с миром только через LAN а millis() примерно через 50 дней переполнится (с обнулением)... смущает это неконтроллируемое примерно

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

 Можете посмотреть на исходик миллис в файле hardware\arduino\cores\arduino\wiring.c

И попробовать обнулить timer0_millis, если она публична (только прерывания не забыть запретить при этом).

 

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

НАСТОЯЩИЕ_ВРЕМЯ=millis()+МАКСИМАЛЬНОЕ_ЗНАЧЕНИЕ+ (МАКСИМАЛЬНОЕ_ЗНАЧЕНИЕ-old_millis)

Правда встает вопрос в какой тип влезет это "натоящие время" :( 

Тут прийдется либо искать библиотеку которая имеет типы для работы с очень большими числами, либо писать свою. Можно еще попробовать поигратся с типом uint64_t, но очень вероятно что ардуина его не поймет. А если поймет - многие функции стандартные будет с ним глючить.

Так что, скорее всего, прийдется заводить отдельную переменнуюдля "счетчик переполнений". И держать отдельную переменную типа "поправка на ветер", куда добавлять каждый раз при "переходе через границу" МАКСИМАЛЬНОЕ_ЗНАЧЕНИЕ-old_millis

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

 2eldev: и кстати, а какое отношение переполнение millis() имеет к изначальной теме "уменьшить размер скетча"? Лучше было отдельную тему завести.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Ну, если порыться в потрохах IDE (а это нужно делать, когда хочется изменить поведение чисто ардуинских функций) ;) ... то находим определение milles():

unsigned long millis()
{
	unsigned long m;
	uint8_t oldSREG = SREG;

	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	cli();
	m = timer0_millis;
	SREG = oldSREG;

	return m;
}

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

void zero_millis()
{
	uint8_t oldSREG = SREG;

	// disable interrupts while we read timer0_millis or we might get an
	// inconsistent value (e.g. in the middle of a write to timer0_millis)
	cli();
	timer0_millis=0L;
	SREG = oldSREG;

}

 О... я еще и опоздал с ответом :)

Проверил - работает. Только функцию нужно расположить там же, где и millis(), потому что только там доступна переменная timer0_millis, и добавить ее определение в arduino.h

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

 >О... я еще и опоздал с ответом :)

В этом нет ничего страшного. Чай не на брейринге. А если два человека ответят "одно и тоже" - все равно не плохо. Чуть-чуть ответы будут отличатся в любом случае. Хотя-бы стилем. Одним людям один ответ понятней будет, другим - другой :) Даже при одинаковой сути.

eldev
Offline
Зарегистрирован: 14.05.2012

leshak пишет:

 2eldev: и кстати, а какое отношение переполнение millis() имеет к изначальной теме "уменьшить размер скетча"? Лучше было отдельную тему завести.

про millis() я начал спрашивать в контексте того чтобы попытаться исключить ntp и rts (в которую еще как-то нужно будет ввести текущее время без ntp и serial подключения) для уменьшения размера скетча и чтобы разобраться с учетом времени

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

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

 >и тогда наверное не понядобятся очень длинные переменные

От них избавится можно еще переходом от "абсолютного времени" к "интервалам". То есть писать в лог, "сколько прошло времени с предыдущего события". Вряд ли интервалы будут больше 49 дней.

Хранить счетчик только на роутере - плохая идея. В этом случае зачем вам вообще время на ардуине? Пусть роутер на свои часы и смотрит.

Вам же millis() нужно, фактически, именно для ситуации когда "связь с роутером пропала", что-бы потом по логам с карты понять "что и когда происходило".

Я бы, наверное делал так: счетчик переполнений и текущие время (с поправкой на ветер) - писал-бы на карту и слал роутеру.

Он бы пихал это в базу+свое время.

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

Тогда, после обрыва связи, посмотрев на карту и найдя там записиь типа

sessionID=456, owerflowConuter=2, fixed_millis=5000

А потом найдя в логе роутера что-то типа

sessionID=456,owerflowConuter=0,fixed_millis=100,time=24.08.2012 10:31:15.1234

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