ArduinoIDE: platzsparende snprintf Alternative

Ich hab ein kleines Problem mit einem Sketch den ich versuche zu kompilieren:

//----------------------------------------------------------------------------------------------------------------------
// TinyTX - An ATtiny84 and RFM12B Wireless Temperature Sensor Node
// By Nathan Chantrell. For hardware design see http://nathan.chantrell.net/tinytx
//
// Using the Dallas DS18B20 temperature sensor with a 4K7 resistor
//
// Requires Arduino IDE with arduino-tiny core: http://code.google.com/p/arduino-tiny/
// and small change to OneWire library, see: http://arduino.cc/forum/index.php/topic,91491.msg687523.html#msg687523
//----------------------------------------------------------------------------------------------------------------------
// modified by meigrafd @ 26.12.2013
//------------------------------------------------------------------------------
#include <RFM12B.h>
#include <avr/sleep.h>
#include <OneWire.h> // http://www.pjrc.com/teensy/arduino_libraries/OneWire.zip
#include <DallasTemperature.h> // http://download.milesburton.com/Arduino/MaximTemperature/DallasTemperature_LATEST.zip
//------------------------------------------------------------------------------

#define NODEID         1  // RF12 node ID in the range 1-30
#define NETWORKID    210  // RF12 Network group
#define FREQ RF12_433MHZ  // Frequency of RFM12B module
#define GATEWAYID     22  // the node ID we're sending to

#define ACK_TIME    2000  // Number of milliseconds to wait for an ack
#define SENDDELAY 300000  // wait this many ms between sending packets
#define requestACK  true  // request ACK? (true/false)
//------------------------------------------------------------------------------
// PIN-Konfiguration 
//------------------------------------------------------------------------------
// SENSOR pins
#define ONE_WIRE_BUS 10   // DS18B20 Temperature sensor is connected on D10/ATtiny pin 13
#define ONE_WIRE_POWER 9  // DS18B20 Power pin is connected on D9/ATtiny pin 12
// LED pin
#define LEDpin 8          // D8, PA2 (ATtiny pin 11) - set to 0 to disable LED
//------------------------------------------------------------------------------
/*
                     +-\/-+
               VCC  1|    |14  GND
          (D0) PB0  2|    |13  AREF (D10)
          (D1) PB1  3|    |12  PA1 (D9)
             RESET  4|    |11  PA2 (D8)
INT0  PWM (D2) PB2  5|    |10  PA3 (D7)
      PWM (D3) PA7  6|    |9   PA4 (D6)
      PWM (D4) PA6  7|    |8   PA5 (D5) PWM
                     +----+
*/

//encryption is OPTIONAL
//to enable encryption you will need to:
// - provide a 16-byte encryption KEY (same on all nodes that talk encrypted)
// - to call .Encrypt(KEY) to start encrypting
// - to stop encrypting call .Encrypt(NULL)
//#define KEY   "ABCDABCDABCDABCD"
//------------------------------------------------------------------------------

// Need an instance of the Radio Module
RFM12B radio;

// Setup a oneWire instance
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature
DallasTemperature sensors(&oneWire);

// Temperatur-String zum Versand per 433 Mhz
char msg[26];

//##############################################################################

//--------------------------------------------------------------------------------------------------
// Read current supply voltage
//--------------------------------------------------------------------------------------------------
long readVcc() {
   bitClear(PRR, PRADC); ADCSRA |= bit(ADEN); // Enable the ADC
   long result;
   // Read 1.1V reference against Vcc
   #if defined(__AVR_ATtiny84__) 
     ADMUX = _BV(MUX5) | _BV(MUX0); // For ATtiny84
   #else
     ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);  // For ATmega328
   #endif 
   delay(2); // Wait for Vref to settle
   ADCSRA |= _BV(ADSC); // Convert
   while (bit_is_set(ADCSRA,ADSC));
   result = ADCL;
   result |= ADCH<<8;
   result = 1126400L / result; // Back-calculate Vcc in mV
   ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable the ADC to save power
   return result;
} 
//########################################################################################################################

void setup() {
  radio.Initialize(NODEID,FREQ,NETWORKID); // Initialize RFM12 with settings defined above 
  #ifdef KEY
    radio.Encrypt((byte*)KEY);
  #endif
  radio.Control(0xC040);           // Adjust low battery voltage to 2.2V
  radio.Sleep(0);                  // Put the RFM12 to sleep

  pinMode(ONE_WIRE_POWER, OUTPUT); // set power pin for DS18B20 to output

  // Start up the library
  sensors.begin();

  PRR = bit(PRTIM1); // only keep timer 0 going
  ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable the ADC to save power
}

void loop() {
  digitalWrite(ONE_WIRE_POWER, HIGH); // turn sensor on

  delay(50); // Allow 50ms for the sensor to be ready
  sensors.requestTemperatures(); // Get the temperature
  int tempsensors.getTempCByIndex(0)*100); // Read first sensor and convert to integer, reversed at receiving end
  //int temp2sensors.getTempCByIndex(1)*100); // read second sensor.. you may have multiple and count them upon startup but I only need one

  digitalWrite(ONE_WIRE_POWER, LOW); // turn sensor off
  
  int supplyV = readVcc(); // Get supply voltage

  // msg-Variable mit Daten zum Versand fuellen, die spaeter an das WebScript uebergeben werden
  snprintf(msg, 26, "v=%d&t=%d", supplyV,temp);

  // Send data via RF
  radio.Wakeup();
  radio.Send(GATEWAYID, (uint8_t *)msg, strlen(msg), requestACK);
  radio.SendWait(2);    //wait for RF to finish sending (2=standby mode)
  radio.Sleep();

  delay(SENDDELAY);
}

Mein Problem ist das der Sketch für den ATtiny84 AVR zu gross ist: region text overflowed by 398 bytes

Sobald ich snprintf(msg, 26, "v=%d&t=%d", supplyV,temp); auskommentiere kommt:

Binäre Sketchgröße: 6.878 Bytes (von einem Maximum von 8.192 Bytes)

Nun hab ich nach einer snprintf Alternative gesucht und mini-printf gefunden - allerdings krieg ich das nicht eingebunden =(

Als Fehlermeldung kommt:

C:\Program Files\Arduino/DS18B20.ino:121: undefined reference to `mini_snprintf(char*, unsigned int, char*, ...)'

Ich hatte auch schon versucht die mini-printf.c in mini-printf.cpp umzubenennen aber dann kommen noch mehr Fehlermeldungen =(

Habe das mini-printf-master.zip geladen, in mein Sketchbook\libraries\ entpackt, Ordner umbenannt in "miniprintf" und im Sketch #include "mini-printf.h" eingefügt...

Alle anderen Libs die in dem Sketch durch #include geladen werden, sind ebenfalls in dem Ordner:
Datei -> Einstellungen -> Sketchbook Speicherort: G:_FUNK_SENSOR\sketches
Und in diesem Ordner sind zwei weitere für meine Sketches wichtige: hardware und libraries

Die Verzeichnis-Struktur im Sketchbook Ordner sieht wie folgt aus:

- hardware
`- attiny
  `-- bootloaders
  |-- cores
  |-- variants

- libraries
`- RFM12B
|- JeeLib
|- DHT22
|- DallasTemperature
|- OneWire
|- miniprintf

Was hab ich vergessen? :s

Kann mir da bitte jemand auf die Sprünge helfen?
Vielen Dank! :wink:

Was hab ich vergessen? :s

Die Arduino IDE neu starten und/oder "Library importieren" ?

Ordner sollte genauso heissen ( "mini-printf" ) ... ?

Libraries besser mit

** **#include <mini-printf.h>** **

einbinden (spitze Klammern).
Kannst auch (erstmal) die zwei Dateien zu deinem Problem-Sketch ins Verzeichnis packen, da passt dann
**#include "mini-printf.h" ** mit ( " " statt spitzer Klammern ) besser.

michael_x:
Die Arduino IDE neu starten

ArduinoIDE wurde neu gestarte, ja

michael_x:
und/oder "Library importieren" ?

Das hat leider auch nichts bewirkt

michael_x:
Ordner sollte genauso heissen ( "mini-printf" ) ... ?

Der Ordner darf kein Bindestrich enthalten sonst meckert ArduinoIDE beim starten. Benenne ich die Dateien ohne Bindestrich um (und pass das auch in der *.c an) kommt die selbe Fehlermeldung wie zuvor undefined reference to `mini_snprintf(char*, unsigned int, char*, ...)'

Das Laden der Header Datei funktioniert aber ja auch, sonst käm eine Meldung von wegen "No such File or Directory"

michael_x:
Libraries besser mit

** **#include <mini-printf.h>** **

einbinden (spitze Klammern).

Das hat leider auch nichts bewirkt :frowning:

michael_x:
Kannst auch (erstmal) die zwei Dateien zu deinem Problem-Sketch ins Verzeichnis packen, da passt dann
**#include "mini-printf.h" ** mit ( " " statt spitzer Klammern ) besser.

Aha das wusst ich noch nicht - hat aber leider auch nichts gebracht :frowning:

Hab gesehen das die ganzen Arduino Libs kein *.c sondern nur *.cpp dateien haben - bei der mini-printf is aber nur eine *.c dabei.. Wenn ich die aber einfach in *.cpp umbenenne krieg ich haufenweise fehlermeldungen :frowning:
Muss man das irgendwie "konvertieren"? Wenn ja, wie?

meigrafd:
Kann mir da bitte jemand auf die Sprünge helfen?

Warum brauchst Du für so eine kleine Formatierungsaufgabe eine Riesenfunktion mit so viel Overhead?
Was sind supplyV und temp für Datentypen? Integer?

char msg[26];

void formatmsg(int supplyV, int temp)
{
//  memset(msg,0,sizeof(msg));
  strcpy(msg,"v=");
  itoa(supplyV,&msg[strlen(msg)],10);
  strcat(msg,"&t=");
  itoa(temp,&msg[strlen(msg)],10);
}

Wenn Du den Puffer nach dem abschließenden Nullzeichen hübsch ausgenullt haben möchtest und noch ein paar Bytes Programmgröße übrig hast, kannst Du die memset-Zeile entkommentieren.

jurs:
Warum brauchst Du für so eine kleine Formatierungsaufgabe eine Riesenfunktion mit so viel Overhead?

Weil mir kein anderer Weg (bis gerade) bekannt war :slight_smile:
Ich nutze halt verschiedene Sensoren also kann ich beim Empfänger keine feste struct'ur erwarten weil die meisten Sensoren nur supplyV und temp übermitteln und 2 auch noch hum...

Über den von mir gewählten Weg kann ich variabel festlegen und Empfänger reicht das empfangene direkt an eine php Datei weiter :wink:

jurs:
Was sind supplyV und temp für Datentypen? Integer?

char msg[26];

void formatmsg(int supplyV, int temp)
{
//  memset(msg,0,sizeof(msg));
  strcpy(msg,"v=");
  itoa(supplyV,&msg[strlen(msg)],10);
  strcat(msg,"&t=");
  itoa(temp,&msg[strlen(msg)],10);
}

Supergeil - funktioniert! Vielen Dank! :slight_smile:

Was genau bewirkt denn die "10" in den beiden Befehlen?

jurs:
Wenn Du den Puffer nach dem abschließenden Nullzeichen hübsch ausgenullt haben möchtest und noch ein paar Bytes Programmgröße übrig hast, kannst Du die memset-Zeile entkommentieren.

Hm ich wüsste jetzt zwar nicht wofür aber wie gesagt - vielen Dank! :slight_smile:

Das ist die Basis des Zahlensystems:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga4f6b3dd51c1f8519d5b8fce1dbf7a665

Die Funktion kann man auch verwenden um um Binär- und Hex-Strings zu erhalten. Aber auch für exotische Zahlensystem wie Basis 12 oder 20.

meigrafd:
Was genau bewirkt denn die "10" in den beiden Befehlen?

Das steht in der Library-Dokumentation des Befehls auf dieser Seite:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga4f6b3dd51c1f8519d5b8fce1dbf7a665

Die "10" steht in dem Fall dafür, dass die Zahl im 10er Dezimalsystem formatiert werden soll.

Wenn Du stattdessen lieber eine Formatierung im Hexadezimalsystem zur Basis 16 hättest, würdest Du da den Parameter "16" statt "10" an die Funktion itoa übergeben.