Hallo,
ich habe mir mit einem Arduino Mini Pro 3.3V (https://de.aliexpress.com/wholesale?catId=0&initiative_id=AS_20190621231745&SearchText=arduino+mini+pro+3.3v), einem 0,96" OLED-I2C-Display (https://de.aliexpress.com/wholesale?catId=0&initiative_id=AS_20190621231830&SearchText=oled+0.96+i2c) und dem Sensor BME280 (https://de.aliexpress.com/wholesale?catId=0&initiative_id=SB_20190621231828&SearchText=bme280) eine kleine Wetterstation gebaut, welche ihre Daten auch per 433MHz-Funkmodul (https://de.aliexpress.com/wholesale?catId=0&initiative_id=SB_20190621231951&SearchText=433mhz+sender) versendet.
Das ganze wird mit einem LiPo betrieben, der über ein entsprechendes Solarmodul (https://de.aliexpress.com/wholesale?catId=0&initiative_id=SB_20190621232208&SearchText=solar+module+6v) mit einem Laderegler (https://de.aliexpress.com/wholesale?catId=0&initiative_id=SB_20190621232143&SearchText=solar+lipo+cn3065) geladen wird.
Zur Senkung des Stromverbrauches (https://andreasrohner.at/posts/Electronics/How-to-modify-an-Arduino-Pro-Mini-clone-for-low-power-consumption/) 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