Solar-Wetterstation mit Funk und Spannungsmesser (Arduino Mini)

Begonnen von dad401, 22 Juni 2019, 09:34:31

Vorheriges Thema - Nächstes Thema

dad401

Hallo,

ich habe mir mit einem Arduino Mini Pro 3.3V, einem 0,96" OLED-I2C-Display und dem Sensor BME280 eine kleine Wetterstation gebaut, welche ihre Daten auch per 433MHz-Funkmodul versendet.
Das ganze wird mit einem LiPo betrieben, der über ein entsprechendes Solarmodul mit einem Laderegler geladen wird.

Zur Senkung des Stromverbrauches des Mini habe ich zusätzlich die LED (bzw. besser: den Widerstand für die LED) entfernt. Damit komme ich im SLEEP-Mode in den uA-Bereich. Den Spannungsregler habe ich gelassen, da ich diesen auch verwende.

Das ganze läuft bisher sehr gut. Sicher gibt es hier und da bestimmt Optimierungspotenzial im Code. Kann jeder gerne ändern und weiternutzen falls Interesse besteht.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#include <LowPower.h>

//#define DEBUG
#define SENSOR_ID 23
#define TX_PWR_PIN 8
#define BUTTON_PIN 2

// enable tx feature
#define ENABLE_TX

// use radiohead/TinyHead or virtual wire
#define USE_RADIOHEAD

// use U8G Lib or Adafruit for OLED
//#define ADA_OLED

#ifdef ENABLE_TX
  #define TX_PIN 11
  #ifdef USE_RADIOHEAD
    #include <RH_ASK.h>
    #ifdef RH_HAVE_HARDWARE_SPI
    #include <SPI.h> // Not actually used but needed to compile
    #endif
   
    // using default pins RH_ASK(uint16_t speed = 2000, uint8_t rxPin = 11, uint8_t txPin = 12, uint8_t pttPin = 10, bool pttInverted = false);
    // -1 => not used
    RH_ASK driver(2000, -1, TX_PIN);
  #else
    #include <VirtualWire.h>
  #endif
#endif

Adafruit_BME280 bme280; // I2C
bool bme280_init_failed = 0;
bool wake_by_button;

struct sdata {
  byte id;
  float temp;
  float hum;
  float pressure;
  float vSolar;
  float vLiPo;
};
sdata sensor_data;

float dewpoint, vin;
unsigned int calls = 0;

// location height
const int altitude = 487;


volatile unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
volatile unsigned long debounceDelay = 100;    // the debounce time; increase if the output flickers

#define temperature_width 18
#define temperature_height 47
#define humid_width 35
#define humid_height 48

#ifdef ADA_OLED
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>

  #define OLED_RESET 4
  Adafruit_SSD1306 display(OLED_RESET);
 
  #if (SSD1306_LCDHEIGHT != 64)
  #error("Height incorrect, please fix Adafruit_SSD1306.h!");
  #endif

  static const unsigned char PROGMEM humid_bits[] = {
    0x0, 0x0, 0xC0, 0x0, 0x0, 0x0, 0x0, 0xE0, 0x0, 0x0, 0x0, 0x1,
    0xF0, 0x0, 0x0, 0x0, 0x3, 0xF8, 0x0, 0x0, 0x0, 0x7, 0xB8, 0x0,
    0x0, 0x0, 0x7, 0x1C, 0x0, 0x0, 0x0, 0xF, 0x1E, 0x0, 0x0, 0x0,
    0x1E, 0xF, 0x0, 0x0, 0x0, 0x1C, 0x7, 0x0, 0x0, 0x0, 0x38, 0x7,
    0x80, 0x0, 0x0, 0x78, 0x3, 0xC0, 0x0, 0x0, 0xF0, 0x1, 0xC0, 0x0,
    0x0, 0xE0, 0x0, 0xE0, 0x0, 0x1, 0xE0, 0x0, 0xF0, 0x0, 0x3, 0xC0,
    0x0, 0x70, 0x0, 0x3, 0x80, 0x0, 0x38, 0x0, 0x7, 0x80, 0x0, 0x3C,
    0x0, 0x7, 0x0, 0x0, 0x1C, 0x0, 0xE, 0x0, 0x0, 0x1E, 0x0, 0x1E,
    0x0, 0x0, 0xE, 0x0, 0x1C, 0x0, 0x0, 0x7, 0x0, 0x1C, 0x0, 0x0,
    0x7, 0x0, 0x38, 0x0, 0x0, 0x3, 0x80, 0x38, 0x0, 0x0, 0x3, 0x80,
    0x70, 0x0, 0x0, 0x3, 0xC0, 0x70, 0x0, 0x0, 0x1, 0xC0, 0x70, 0x0,
    0x0, 0x1, 0xC0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0,
    0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0,
    0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0xE0, 0x0, 0x0,
    0x0, 0xE0, 0xE0, 0x0, 0x0, 0x0, 0xE0, 0x70, 0x0, 0x0, 0x1, 0xC0,
    0x70, 0x0, 0x0, 0x1, 0xC0, 0x78, 0x0, 0x0, 0x3, 0xC0, 0x38, 0x0,
    0x0, 0x3, 0x80, 0x3C, 0x0, 0x0, 0x7, 0x80, 0x1E, 0x0, 0x0, 0xF,
    0x0, 0xF, 0x0, 0x0, 0x1E, 0x0, 0x7, 0x80, 0x0, 0x3C, 0x0, 0x3,
    0xE0, 0x0, 0xFC, 0x0, 0x1, 0xF8, 0x3, 0xF0, 0x0, 0x0, 0xFF, 0xFF,
    0xE0, 0x0, 0x0, 0x3F, 0xFF, 0x80, 0x0, 0x0, 0x3, 0xFC, 0x0, 0x0 };
 
  static const unsigned char PROGMEM  temperature_bits[]  = {
    0x3, 0xF0, 0x0, 0x7, 0xF8, 0x0, 0xE, 0x1C, 0x0, 0xC, 0xC, 0x0,
    0xC, 0xC, 0x0, 0xC, 0xC, 0x0, 0xC, 0xC, 0x0, 0xC, 0xC, 0x0,
    0xC, 0xC, 0x0, 0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xC, 0xC, 0x0,
    0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xC, 0xC, 0x0, 0xC, 0xC, 0x0,
    0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xC, 0xC, 0x0, 0xD, 0xEC, 0x0,
    0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xC, 0xC, 0x0, 0xD, 0xEC, 0x0,
    0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0,
    0xD, 0xEC, 0x0, 0xD, 0xEC, 0x0, 0x19, 0xE6, 0x0, 0x31, 0xE3, 0x0,
    0x63, 0xF1, 0x80, 0x47, 0xF8, 0x80, 0xCF, 0xFC, 0xC0, 0xCF, 0xFC, 0xC0,
    0xCF, 0xFC, 0xC0, 0xCF, 0xFC, 0xC0, 0xCF, 0xFC, 0xC0, 0xCF, 0xFC, 0xC0,
    0xCF, 0xFC, 0xC0, 0x67, 0xF9, 0x80, 0x63, 0xF1, 0x80, 0x30, 0x3, 0x0,
    0x1C, 0xE, 0x0, 0xF, 0xFC, 0x0, 0x3, 0xF0, 0x0 };
#else

  #include "U8glib.h"

  // setup u8g object, please remove comment from one of the following constructor calls
  // IMPORTANT NOTE: The following list is incomplete. The complete list of supported
  // devices with all constructor calls is here: https://github.com/olikraus/u8glib/wiki/device
  U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK);  // Display which does not send AC

  static const unsigned char humid_bits[] U8G_PROGMEM = {
     0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x80,
     0x0f, 0x00, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0xe0, 0x1d, 0x00,
     0x00, 0x00, 0xe0, 0x38, 0x00, 0x00, 0x00, 0xf0, 0x78, 0x00, 0x00, 0x00,
     0x78, 0xf0, 0x00, 0x00, 0x00, 0x38, 0xe0, 0x00, 0x00, 0x00, 0x1c, 0xe0,
     0x01, 0x00, 0x00, 0x1e, 0xc0, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x03, 0x00,
     0x00, 0x07, 0x00, 0x07, 0x00, 0x80, 0x07, 0x00, 0x0f, 0x00, 0xc0, 0x03,
     0x00, 0x0e, 0x00, 0xc0, 0x01, 0x00, 0x1c, 0x00, 0xe0, 0x01, 0x00, 0x3c,
     0x00, 0xe0, 0x00, 0x00, 0x38, 0x00, 0x70, 0x00, 0x00, 0x78, 0x00, 0x78,
     0x00, 0x00, 0x70, 0x00, 0x38, 0x00, 0x00, 0xe0, 0x00, 0x38, 0x00, 0x00,
     0xe0, 0x00, 0x1c, 0x00, 0x00, 0xc0, 0x01, 0x1c, 0x00, 0x00, 0xc0, 0x01,
     0x0e, 0x00, 0x00, 0xc0, 0x03, 0x0e, 0x00, 0x00, 0x80, 0x03, 0x0e, 0x00,
     0x00, 0x80, 0x03, 0x07, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x00,
     0x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x07,
     0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00,
     0x00, 0x07, 0x07, 0x00, 0x00, 0x00, 0x07, 0x0e, 0x00, 0x00, 0x80, 0x03,
     0x0e, 0x00, 0x00, 0x80, 0x03, 0x1e, 0x00, 0x00, 0xc0, 0x03, 0x1c, 0x00,
     0x00, 0xc0, 0x01, 0x3c, 0x00, 0x00, 0xe0, 0x01, 0x78, 0x00, 0x00, 0xf0,
     0x00, 0xf0, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x01, 0x00, 0x3c, 0x00, 0xc0,
     0x07, 0x00, 0x3f, 0x00, 0x80, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0xff, 0xff,
  0x07, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00 };

  static const unsigned char temperature_bits[] U8G_PROGMEM = {
   0xc0, 0x0f, 0x00, 0xe0, 0x1f, 0x00, 0x70, 0x38, 0x00, 0x30, 0x30, 0x00,
   0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00,
   0x30, 0x30, 0x00, 0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0x30, 0x30, 0x00,
   0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x00,
   0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0x30, 0x30, 0x00, 0xb0, 0x37, 0x00,
   0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0x30, 0x30, 0x00, 0xb0, 0x37, 0x00,
   0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00,
   0xb0, 0x37, 0x00, 0xb0, 0x37, 0x00, 0x98, 0x67, 0x00, 0x8c, 0xc7, 0x00,
   0xc6, 0x8f, 0x01, 0xe2, 0x1f, 0x01, 0xf3, 0x3f, 0x03, 0xf3, 0x3f, 0x03,
   0xf3, 0x3f, 0x03, 0xf3, 0x3f, 0x03, 0xf3, 0x3f, 0x03, 0xf3, 0x3f, 0x03,
   0xf3, 0x3f, 0x03, 0xe6, 0x9f, 0x01, 0xc6, 0x8f, 0x01, 0x0c, 0xc0, 0x00,
  0x38, 0x70, 0x00, 0xf0, 0x3f, 0x00, 0xc0, 0x0f, 0x00 };
#endif
/*
byte flipByte(byte c){
  char r=0;
  for(byte i = 0; i < 8; i++){
    r <<= 1;
    r |= c & 1;
    c >>= 1;
  }
  return r;
}

void revertBits(uint8_t *src, uint8_t *dst, int iSize) {
  for (int i = 0; i < iSize; i++) {     
    dst = flipByte(src);   
  }
}
*/

void setup()   {               
  Serial.begin(115200);

/*
  for (int i = 0; i < sizeof(humid_bits); i++) {
    if (i % 12 == 0) {
      Serial.println("");
    }
    Serial.print("0x");
    Serial.print(flipByte(pgm_read_byte(&humid_bits[i])), HEX); 
    Serial.print(", ");
  }
*/

  pinMode(BUTTON_PIN, INPUT_PULLUP);
 
  // init values
  sensor_data.id = SENSOR_ID;
  sensor_data.temp = -1;
  sensor_data.hum = -1;
  sensor_data.pressure = -1;
  sensor_data.vSolar = -1;
  sensor_data.vLiPo = -1;
  #ifdef ENABLE_TX
    pinMode(TX_PWR_PIN, OUTPUT);
    digitalWrite(TX_PWR_PIN, HIGH);
    wake_by_button = 0;
  #else
    wake_by_button = 1;
  #endif
  dewpoint = 0;
  vin = 0;
   
  Serial.println(F("init sensor bme280..."));
  if (!initBME280(0x76) && !initBME280(0x77)) {
    Serial.println(F("Could not find a valid BME280 sensor, check wiring!"));
    bme280_init_failed = 1;
  } else {
    Serial.println(F("BME280 sensor found!"));
  }

  #ifdef ENABLE_TX
    Serial.println(F("init transmitter..."));
    #ifdef USE_RADIOHEAD
      if (!driver.init())
        Serial.println(F("init failed"));
    #else
      // Initialise the IO and ISR
      vw_set_ptt_inverted(true); // Required for DR3100
      vw_set_tx_pin(TX_PIN);
      vw_setup(2000);   // Bits per sec
    #endif
  #endif
 
  Serial.println(F("init display..."));
  #ifdef ADA_OLED
    // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
    // init done
 
    display.ssd1306_command(SSD1306_SETCONTRAST);
    display.ssd1306_command(250); // Where c is a value from 0 to 255 (sets contrast e.g. brightness)   

    // Show image buffer on the display hardware.
    // Since the buffer is intialized with an Adafruit splashscreen
    // internally, this will display the splashscreen.
    //display.display();
    //delay(2000);
 
    // Clear the buffer.
    display.clearDisplay();
  #else
    u8g.begin();
  #endif

  #ifdef ENABLE_TX
    // turn off display
    #ifdef ADA_OLED
      display.ssd1306_command(SSD1306_DISPLAYOFF);
    #else
      u8g.sleepOn();
    #endif
  #endif
}

void loop() {
   if (!bme280_init_failed) {
    #ifdef DEBUG
      Serial.println(F("read BME data"));
    #endif     
    read_BME();

    if (wake_by_button) {
      drawTemp(sensor_data.temp, 0);
      delay(5000);
 
      drawHumid();
      delay(5000);
     
      drawTemp(dewpoint, 1);
      delay(5000);
 
      drawPress();
      delay(5000);
    }
  } else {
    #ifdef DEBUG
      Serial.println(F("Could not find a valid BME280 sensor, check wiring!"));
    #endif     

    // reset data to send
    sensor_data.temp = -1;
    sensor_data.hum = -1;
    sensor_data.pressure = -1;
    sensor_data.vSolar = -1;
    sensor_data.vLiPo = -1;

    if (wake_by_button) {
      drawError();
      delay(5000);
    }
  }
    //serialPrint();
    read_voltages();

    if (wake_by_button) {
      calls++;
      drawSystem();
      delay(5000);
    }

    #ifdef ENABLE_TX
      sendData();
    #endif

    wake_by_button = 0;
   
    enableSleep();
    disableSleep();

    delay(3000);
}

void enableSleep() {
  #ifdef DEBUG
    Serial.println(F("enable sleep mode"));
  #endif     

  #ifdef ADA_OLED
    display.clearDisplay();
    // turn off display
    display.ssd1306_command(SSD1306_DISPLAYOFF);
  #else
    u8g.sleepOn();
  #endif

  #ifdef ENABLE_TX
    // disable transmitter
    digitalWrite(TX_PWR_PIN, LOW);
    pinMode(TX_PWR_PIN, INPUT);
    // let voltage settle on pin
    delay(200);
  #endif
 
  // set sleep mode for BME280
  bme280.setSampling(Adafruit_BME280::MODE_SLEEP, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_0_5);

  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);

  #ifdef ENABLE_TX
  // 30min/15min = 30x60 = 1800/900 s
  // 1800/900 s / 8 s = 225/112,5
  unsigned int sleepCounter;
  for (sleepCounter = 113; sleepCounter > 0; sleepCounter--)
  {
    // Enter power down state with ADC and BOD module disabled.
    // Wake up when wake up pin is low.
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
    if (wake_by_button)
      break;
  }
  #else
    // Enter power down state with ADC and BOD module disabled.
    // Wake up when wake up pin is low.
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  #endif
  detachInterrupt(digitalPinToInterrupt(BUTTON_PIN));
}

void disableSleep() {
  #ifdef DEBUG
    Serial.println(F("disable sleep mode"));
    Serial.println(F("init sensor bme280..."));
  #endif     
  if (!initBME280(0x76) && !initBME280(0x77)) {
    #ifdef DEBUG
      Serial.println(F("Could not find a valid BME280 sensor, check wiring!"));
    #endif
    bme280_init_failed = 1;
  } else {
    #ifdef DEBUG
      Serial.println(F("BME280 sensor found!"));
    #endif
    // sensor BME280
    bme280.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X4, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_0_5);
  }

  if (wake_by_button) {
    #ifdef ADA_OLED
      // turn on display
      display.ssd1306_command(SSD1306_DISPLAYON);
   
      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(WHITE);
      display.setCursor(0,0);
   
      //letter count     1234567890 (max for text size 2)
      display.println(F("AUFGEWACHT"));
      display.setTextSize(1);
      display.setCursor(0,30);
      display.println(F("rufe Sensordaten ab"));
      display.println(F("       ..."));
      display.display();
    #else
      u8g.sleepOff();
      u8g.begin();

      u8g.firstPage();
      do {
        u8g.setFont(u8g_font_fub14r);
        u8g.setPrintPos(10, 15);
        u8g.print("GEWECKT");
     
        u8g.setFont(u8g_font_helvB08r);
 
        u8g.setPrintPos(10, 40);
        u8g.print(F("rufe Sensordaten ab!"));
      } while( u8g.nextPage() );
   #endif
  }
  #ifdef ENABLE_TX
    // enable transmitter
    pinMode(TX_PWR_PIN, OUTPUT);
    digitalWrite(TX_PWR_PIN, HIGH);
    // let voltage settle on pin
    delay(1000);
  #endif
}

void drawTemp(float temp, bool dp) {
  #ifdef ADA_OLED
    // Clear the buffer
    display.clearDisplay();
   
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
 
    if (dp) {
      //letter count     1234567890 (max for text size 2)
      display.println(F("TAUP C 3/5"));
    } else {
      //letter count     1234567890 (max for text size 2)
      display.println(F("TEMP C 1/5"));
    }
 
    // Celcius symbol
    display.drawCircle(54, 3, 3,  WHITE);
 
    // text size 1 = 5(+1 space)x8(+1 space) pixel
    display.setTextSize(3);             // Normal 1:1 pixel scale
    display.setCursor(5, 30);             // Start at top-left corner
 
    char temp_str[6];
    if (round(temp*10)/10.0 > -10) {
      display.setCursor(5, 30);             // Start at top-left corner
      dtostrf(temp, 5, 2, temp_str);
      display.print(temp_str); 
    } else {
      display.setCursor(5, 30);             // Start at top-left corner
      dtostrf(temp, 5, 1, temp_str);
      display.print(temp_str); 
    }
 
    // Thermometer icon
    display.drawBitmap(100, 16, temperature_bits, temperature_width, temperature_height, 1);
 
    display.display();
  #else
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(0, 15);
      if (dp) {
        //letter count     1234567890 (max for text size 2)
        u8g.print(F("TAUP  C"));
        u8g.drawStr(97, 15, "3/6");
      } else {
        u8g.print(F("TEMP  C"));
        u8g.drawStr(97, 15, "1/6");
      }
      // Celcius symbol
      u8g.drawCircle(64, 3, 3);
   
      u8g.setFont(u8g_font_fub25n);
      u8g.setPrintPos(0, 50);

      char temp_str[6];
      if (round(temp*10)/10.0 > -10) {
        dtostrf(temp, 5, 2, temp_str);
        u8g.print(temp_str); 
      } else {
        dtostrf(temp, 5, 1, temp_str);
        u8g.print(temp_str); 
      }
   
      // graphic commands to redraw the complete screen should be placed here 
      u8g.drawXBMP( 100, 16, temperature_width, temperature_height, temperature_bits);
    } while( u8g.nextPage() );
  #endif
}

void drawHumid(void) {
  #ifdef ADA_OLED
    // Clear the buffer
    display.clearDisplay();
   
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
 
    //letter count     1234567890 (max for text size 2)
    display.println(F("relF % 2/5"));
 
    char hum_str[6];
    // text size 1 = 5(+1 space)x8(+1 space) pixel
    display.setTextSize(3);             // Normal 1:1 pixel scale
    display.setCursor(0, 30);             // Start at top-left corner
    dtostrf(sensor_data.hum, 5, 2, hum_str);
    display.print(hum_str); 
 
    // Humidity icon
    display.drawBitmap(93, 16, humid_bits, humid_width, humid_height, 1);
 
    display.display();
  #else
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(0, 15);
      u8g.print(F("rel. LF %"));
      u8g.drawStr(97, 15, "2/6");
   
      u8g.setFont(u8g_font_fub25n);
      u8g.setPrintPos(0, 50);

      char hum_str[6];
      // text size 1 = 5(+1 space)x8(+1 space) pixel
      dtostrf(sensor_data.hum, 5, 2, hum_str);
      u8g.print(hum_str);
   
       // graphic commands to redraw the complete screen should be placed here 
      u8g.drawXBMP( 93, 16, humid_width, humid_height, humid_bits);
    } while( u8g.nextPage() );
  #endif
}

void drawPress(void) {
  #ifdef ADA_OLED
    // Clear the buffer
    display.clearDisplay();
   
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
 
    //letter count     1234567890 (max for text size 2)
    display.println(F("rel LD 4/5"));
 
    char press_str[8];
   
    // text size 1 = 5(+1 space)x8(+1 space) pixel
    display.setTextSize(3);             // Normal 1:1 pixel scale
    display.setCursor(0, 28);             // Start at top-left corner
    dtostrf(sensor_data.pressure, 7, 2, press_str);
    display.print(press_str); 
 
    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setCursor(0, 55);             // Start at top-left corner
    display.print(F("Hoehenbezug: "));
    display.print(altitude);
    display.println(F(" m"));
   
    display.display();
   #else
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(0, 15);
      u8g.print(F("Luftdruck"));
      u8g.drawStr(97, 15, "4/6");

      char press_str[8];
   
      u8g.setFont(u8g_font_fub25n);
      u8g.setPrintPos(0, 50);
      dtostrf(sensor_data.pressure, 7, 2, press_str);
      u8g.print(press_str); 
   
      u8g.setFont(u8g_font_helvB08r);
      u8g.setPrintPos(10, 62);
      u8g.print(F("Hoehenbezug: "));
      u8g.setPrintPos(85, 62);
      u8g.print(altitude);
      u8g.setPrintPos(104, 62);
      u8g.print(F("m"));
    } while( u8g.nextPage() );
   #endif
}

void drawSystem(void) {
  // readout VCC
  vin = readVcc()/1000.0;

  #ifdef ADA_OLED
    // Clear the buffer
    display.clearDisplay();
   
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(WHITE);        // Draw white text
    display.setCursor(0,0);             // Start at top-left corner
 
    //letter count     1234567890 (max for text size 2)
    display.println(F("SYSTEM 5/5"));
 
    char volt_str[6];
 
    // text size 1 = 5(+1 space)x8(+1 space) pixel
    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.print(F("SolarCell: "));
    dtostrf(sensor_data.vSolar, 5, 3, volt_str);
    display.print(volt_str);
    display.println(F(" V"));
 
    display.print(F("LiPo:      ")); 
    dtostrf(sensor_data.vLiPo, 5, 3, volt_str);
    display.print(volt_str);
    display.println(F(" V"));
 
    display.print(F("Arduino:   "));
    dtostrf(vin, 5, 3, volt_str);
    display.print(volt_str);
    display.println(F(" V"));
 
    display.print(F("FreeRAM:   "));
    display.print(freeRam());
    display.println(F(" B"));
   
    display.print(F("Aufrufe:   "));
    display.println(calls);
 
    display.print(F("Wachzeit:  "));
    display.print(millis()/1000);
    display.println(F(" s"));
   
    display.display();
   #else
    char volt_str[6];

    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(0, 15);
      u8g.print("SPANN.");
      u8g.drawStr(97, 15, "5/6");
   
      u8g.setFont(u8g_font_helvB08r);

      u8g.setPrintPos(0, 30);
      u8g.print(F("SolarCell: "));
      dtostrf(sensor_data.vSolar, 5, 3, volt_str);
      u8g.setPrintPos(55, 30);
      u8g.print(volt_str);
      u8g.drawStr(88, 30, F("Volt"));

      u8g.setPrintPos(0, 46);
      u8g.print(F("LiPo: "));
      dtostrf(sensor_data.vLiPo, 5, 3, volt_str);
      u8g.setPrintPos(55, 46);
      u8g.print(volt_str);
      u8g.drawStr(88, 46, F("Volt"));
     
      u8g.setPrintPos(0, 63);
      u8g.print(F("Arduino: "));
      dtostrf(vin, 5, 3, volt_str);
      u8g.setPrintPos(55, 63);
      u8g.print(volt_str);
      u8g.drawStr(88, 63, F("Volt"));
    } while( u8g.nextPage() );
    delay(10000);
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(0, 15);
      u8g.print("SYSTEM");
      u8g.drawStr(97, 15, "6/6");
   
      u8g.setFont(u8g_font_helvB08r);

      u8g.setPrintPos(0, 30);
      u8g.print(F("FreeRAM: "));
      u8g.setPrintPos(55, 30);
      u8g.print(freeRam());
      u8g.drawStr(88, 30, F("Byte"));

      u8g.setPrintPos(0, 46);
      u8g.print(F("Aufrufe: "));
      u8g.setPrintPos(55, 46);
      u8g.print(calls);
     
      u8g.setPrintPos(0, 63);
      u8g.print(F("Wachzeit: "));
      u8g.setPrintPos(55, 63);
      u8g.print(millis()/1000);
      u8g.drawStr(88, 63, F("s"));
    } while( u8g.nextPage() );

   #endif
}

void drawError() {
  #ifdef ADA_OLED
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
 
    //letter count     1234567890 (max for text size 2)
    display.println(F("Fehler"));
    display.setTextSize(1);
    display.println(F("Sensor nicht"));
    display.println(F("gefunden!"));
    display.display();
  #else
    u8g.firstPage();
    do {
      u8g.setFont(u8g_font_fub14r);
      u8g.setPrintPos(25, 15);
      u8g.print("FEHLER");
   
      u8g.setFont(u8g_font_helvB08r);

      u8g.setPrintPos(30, 30);
      u8g.print(F("Sensor nicht"));

      u8g.setPrintPos(35, 46);
      u8g.print(F("gefunden!"));
    } while( u8g.nextPage() );
  #endif
}

void buttonISR() {
  #ifdef DEBUG
    Serial.println(F("button ISR"));
  #endif
  if ((millis() - lastDebounceTime) > debounceDelay) {
    #ifdef DEBUG
      Serial.println("Button FALLING!");
    #endif
    lastDebounceTime = millis(); // letzte Schaltzeit merken     
    wake_by_button = 1; 
  }
}

#ifdef ENABLE_TX
void sendData() {
    #ifdef DEBUG
      Serial.println(F("send data"));
    #endif
    digitalWrite(13, true); // Flash a light to show transmitting


    // send data 3 times
    #ifdef USE_RADIOHEAD
      driver.send((byte*)&sensor_data, sizeof(sensor_data));
      driver.waitPacketSent();
      delay(1000);
      driver.send((byte*)&sensor_data, sizeof(sensor_data));
      driver.waitPacketSent();
      delay(1000);
      driver.send((byte*)&sensor_data, sizeof(sensor_data));
      driver.waitPacketSent();
    #else
      // Virtual Wire       
      vw_send((byte*)&sensor_data, sizeof(sensor_data));
      vw_wait_tx(); // Wait until the whole message is gone
      delay(1000);
      vw_send((byte*)&sensor_data, sizeof(sensor_data));
      vw_wait_tx(); // Wait until the whole message is gone
      delay(1000);
      vw_send((byte*)&sensor_data, sizeof(sensor_data));
      vw_wait_tx(); // Wait until the whole message is gone
    #endif
    digitalWrite(13, false);
    delay(100);
}
#endif

/*****************************************************************
/* Init BME280                                                   *
/*****************************************************************/
bool initBME280(char addr) {
  Serial.print(F("Trying BME280 sensor on "));
  Serial.println(String(addr, HEX));

  if (bme280.begin(addr)) {
    return true;
  } else {
    return false;
  }
}

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
     ADMUX = _BV(MUX5) | _BV(MUX0) ;
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif 

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH 
  uint8_t high = ADCH; // unlocks both

  long result = (high<< | low;

  //result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  // old meassurements
  //result = 1103850L / result; // Calculate Vcc (in mV); 1125300 = 1.1* (3.345 [voltmeter] / 3.41 [readVcc default])  1023*1000, https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

  // Arduino Mini 2 marked with 1 white point (first oled solar sensor)
  result = 1102148L / result; // Calculate Vcc (in mV); 1125300 = 1.1* (3.380 [voltmeter] / 3.451 [readVcc default])  1023*1000, https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

  // Arduino Mini 2 marked with 2 white points
  //result = 1125633L / result; // Calculate Vcc (in mV); 1125300 = 1.1* (3.380 [voltmeter] / 3.379 [readVcc default])  1023*1000, https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

 
  return result; // Vcc in millivolts
}


int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void read_voltages () {
  // readout VCC
  vin = readVcc()/1000.0;
 
  // max input for A0: 12V => solar cell
  // R1 = 47K, R2 = 18K
  // Vin = 12V => Vout = 3.32V
  sensor_data.vSolar = analogRead(A0) * (vin / 1023.0);

  // (47K+18K) / 18K = 3.6111111111
  // using meassured values: (48.65K + 17.86K) / 17.86K = 3.7239641657
  sensor_data.vSolar = 3.7239641657 * sensor_data.vSolar;

  // max input for A1: 5V => LiPo
  // R1 = 24K, R2 = 47K
  // Vin = 5V => Vout = 3.31V
  sensor_data.vLiPo = analogRead(A1) * (vin / 1023.0);

  // (24K+47K) / 47K = 1.5106382978
  // using meassured values: (24.13K + 48.25K) / 48.25K = 1.5001036269
  sensor_data.vLiPo = 1.5001036269 * sensor_data.vLiPo;
}

void read_BME () {
    sensor_data.temp = bme280.readTemperature();
    sensor_data.hum = bme280.readHumidity();
 
    // Simple approximation
    //dp = t-0.36*(100.0-h); // Fahrenheit
    dewpoint = sensor_data.temp-((100.0-sensor_data.hum)/5); // Celsius
    sensor_data.pressure = bme280.readPressure()/100.0F;
    sensor_data.pressure = sensor_data.pressure / pow(1.0 - altitude/44330.0, 5.255);
}


Hier ein Empfängerbeispiel, welches ich mit einem Arduino Nano mit LAN-Interface nutze:

#include <UIPEthernet.h>

//#define RX_PIN D1
#define RX_PIN 9

//#define DEBUG

// use radiohead/TinyHead or virtual wire
#define USE_RADIOHEAD

#ifdef USE_RADIOHEAD
  #include <Arduino.h>
  #include <RH_ASK.h>
 
  #define RADIOHEAD_BAUD 2000 // Transmission Speed
  #define RADIOHEAD_TX_PIN -1 // Pin of the 433MHz transmitter (here not used) - default: 12
  #define RADIOHEAD_RX_PIN  RX_PIN // Pin of the 433MHz receiver - default: 11
 
  RH_ASK driver(RADIOHEAD_BAUD, RADIOHEAD_RX_PIN, RADIOHEAD_TX_PIN);
#else
  #include <VirtualWire.h>
#endif

struct sdata {
  byte id;
  float temp;
  float hum;
  float pressure;
  float vSolar;
  float vLiPo;
};
sdata sensor_data;
uint8_t buflen;
unsigned long lastmessage;

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xF9, 0xE4
};
IPAddress ip(192, 168, 10, 94);

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
  Serial.begin(115200);

  #ifdef DEBUG
    Serial.println(F("init driver for receiver"));
  #endif
  #ifdef USE_RADIOHEAD
    driver.init();
  #else 
    // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000);   // Bits per sec
    vw_set_rx_pin(RX_PIN);
    vw_rx_start();       // Start the receiver PLL running
  #endif

  buflen = sizeof(sensor_data);
  lastmessage = 0;

  pinMode(LED_BUILTIN, OUTPUT); 

  #ifdef DEBUG
    Serial.println(F("init web server"));
  #endif

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();

  #ifdef DEBUG
    Serial.print(F("server is at "));
    Serial.println(Ethernet.localIP());
    Serial.println(F("setup done"));
  #endif
}


void loop() {
    #ifdef USE_RADIOHEAD
      if (driver.recv((byte*)&sensor_data, &buflen)) { // Non-blocking
    #else
       if (vw_get_message((byte*)&sensor_data, &buflen)) { // Non-blocking
    #endif
        digitalWrite(LED_BUILTIN, HIGH);   // Turn the LED on (Note that LOW is the voltage level
        lastmessage = millis();
        // Message with a good checksum received, dump it.
        #ifdef DEBUG
          Serial.print(F("Got: "));
          Serial.print(F("ID: "));
          Serial.print(sensor_data.id);
          Serial.print(F(" T:"));
          Serial.print(sensor_data.temp);
          Serial.print(F(" H:"));
          Serial.print(sensor_data.hum);
          Serial.print(F(" P:"));
          Serial.print(sensor_data.pressure);
          Serial.print(F(" vS:"));
          Serial.print(sensor_data.vSolar);
          Serial.print(F(" vL:"));
          Serial.println(sensor_data.vLiPo);
        #endif
        delay(500);
        digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level
    }

  // listen for incoming clients
  EthernetClient client = server.available();

  if (client) {
    #ifdef DEBUG
      Serial.println(F("new client connected"));
    #endif
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        #ifdef DEBUG
          Serial.write(c);
        #endif
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close"));  // the connection will be closed after completion of the response
          //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));

          client.print(F("sensor ID: "));
          client.print(sensor_data.id);
          client.println(F("<br />"));
         
          client.print(F("temperature: "));
          client.print(sensor_data.temp);
          client.print(F(" C"));
          client.println(F("<br />"));

          client.print(F("humidity: "));
          client.print(sensor_data.hum);
          client.print(F(" %"));
          client.println(F("<br />"));

          client.print(F("pressure: "));
          client.print(sensor_data.pressure);
          client.print(F(" hPa"));
          client.println(F("<br />"));

          client.print(F("SolarCell: "));
          client.print(sensor_data.vSolar);
          client.print(F(" V"));
          client.println(F("<br />"));

          client.print(F("LiPo: "));
          client.print(sensor_data.vLiPo);
          client.print(F(" V"));
          client.println(F("<br />"));
          client.println(F("<br />"));

          client.print(F("last message seen: "));
          client.print((millis()-lastmessage)/60000);
          client.print(F(" minutes ago"));
          client.println(F("<br />"));

          client.print(F("free ram: "));
          client.println(freeRam());
          client.print(F(" Byte"));
          client.println(F("<br />"));
          client.print(F("voltage: "));
          client.println(readVcc()/1000.0);
          client.print(F(" V"));
          client.println(F("<br />"));
          client.print(F("uptime (minutes): "));
          client.print(millis()/60000);
          client.println(F("<br />"));
          client.println(F("</html>")); 
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    #ifdef DEBUG
      Serial.println(F("client disconnected"));
    #endif
  }
}

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
     ADMUX = _BV(MUX5) | _BV(MUX0) ;
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif 

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH 
  uint8_t high = ADCH; // unlocks both

  long result = (high<< | low;

  //result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000

  // Arduino Nano marked with 1 white point
  result = 1119085L / result; // Calculate Vcc (in mV); 1125300 = 1.1* (4.682 [voltmeter] / 4.708 [readVcc default])  1023*1000, https://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

 
  return result; // Vcc in millivolts
}



Ein paar Bilder sind beigefügt.

Marcus

--
aktuelles FHEM auf einem RPI3 mit
HM-MOD-RPI-PCB, CUL868, nanoCUL (868MHz), Signalduino (433MHz), SignalESP (433 MHz) zur Nutzung folgender Systeme:
FS20/Homematic/IT-Steckdosen/OW/Sonstiges