WIP
Last active
July 25, 2020 07:15
-
-
Save CAFxX/899710ba7144f29e07753def20886100 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const byte CCHAR_SUB_2[8] PROGMEM = { | |
B00000, | |
B00000, | |
B00000, | |
B11000, | |
B00100, | |
B01000, | |
B10000, | |
B11100 | |
}; | |
const byte CCHAR_PLACEHOLDER_START[8] PROGMEM = { | |
B10101, | |
B00000, | |
B10000, | |
B00000, | |
B10000, | |
B00000, | |
B10101, | |
B00000 | |
}; | |
const byte CCHAR_PLACEHOLDER_END[8] PROGMEM = { | |
B10101, | |
B00000, | |
B00001, | |
B00000, | |
B00001, | |
B00000, | |
B10101, | |
B00000 | |
}; | |
const byte CCHAR_PLACEHOLDER_MIDDLE[8] PROGMEM = { | |
B10101, | |
B00000, | |
B00000, | |
B00000, | |
B00000, | |
B00000, | |
B10101, | |
B00000 | |
}; | |
const byte CCHAR_ICON_RADIO_SIGNAL[8] PROGMEM = { | |
B00000, | |
B00000, | |
B11100, | |
B00010, | |
B11001, | |
B00101, | |
B10101, | |
B00000 | |
}; | |
void loadCustomChars() { | |
loadCustomCharP(1, CCHAR_SUB_2); | |
loadCustomCharP(2, CCHAR_PLACEHOLDER_START); | |
loadCustomCharP(3, CCHAR_PLACEHOLDER_END); | |
loadCustomCharP(4, CCHAR_PLACEHOLDER_MIDDLE); | |
loadCustomCharP(5, CCHAR_ICON_RADIO_SIGNAL); | |
} | |
void loadCustomCharP(byte idx, const byte c[8]) { | |
byte buf[8]; | |
memcpy_P(buf, c, 8); | |
lcd.createChar(idx, buf); | |
} | |
bool drawGraph(byte n, byte k, byte *data) { | |
if (n > 8 || n < 1) return false; | |
for (int i = 0; i < n; i++) { | |
byte buf[8]; | |
for (int j = 0; j < 8; j++) { | |
switch (k) { | |
case 1: | |
buf[7 - j] = (data[i * 1 + 0] > j ? B11111 : 0); | |
case 2: | |
buf[7 - j] = (data[i * 2 + 0] > j ? B11000 : 0) | (data[i * 2 + 1] > j ? B00011 : 0); | |
case 3: | |
buf[7 - j] = (data[i * 3 + 0] > j ? B10000 : 0) | (data[i * 3 + 1] > j ? B00100 : 0) | (data[i * 3 + 2] > j ? B00001 : 0); | |
case 6: | |
buf[7 - j] = (data[i * 6 + 1] > j ? B10000 : 0) | (data[i * 6 + 2] > j ? B01000 : 0) | (data[i * 6 + 3] > j ? B00100 : 0) | (data[i * 6 + 4] > j ? B00010 : 0) | (data[i * 6 + 5] > j ? B00001 : 0); | |
default: | |
return false; | |
} | |
} | |
lcd.createChar(i, buf); | |
} | |
return true; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Wire.h> | |
#include <BMx280I2C.h> | |
#include <NeoSWSerial.h> | |
#include <TinyGPS.h> | |
#include <EEPROM.h> | |
#include <IRremote.h> | |
#include <IRremoteInt.h> | |
#include <LowPower.h> | |
#include <LiquidCrystal.h> | |
#include <dht_nonblocking.h> | |
#include <limits.h> | |
#define UNKNOWN_INT (INT_MIN/2) | |
#define UNKNOWN_ULONG (LONG_MIN/2) | |
#define PIN_LCD_RS 12 | |
#define PIN_LCD_ENABLE 11 | |
#define PIN_LCD_D4 5 | |
#define PIN_LCD_D5 4 | |
#define PIN_LCD_D6 3 | |
#define PIN_LCD_D7 2 | |
#define PIN_LCD_BCKLGT 8 | |
#define PIN_DHT 10 | |
#define PIN_IR 7 | |
#define PIN_GPS_RX A0 | |
#define PIN_GPS_TX A1 | |
#define I2C_BMP280 0x76 | |
#define TIMEZONE 9 // hours | |
#define ALTITUDE 35 // m | |
#define DEBUG 1 | |
LiquidCrystal lcd(PIN_LCD_RS, PIN_LCD_ENABLE, PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7); | |
DHT_nonblocking dht(PIN_DHT, DHT_TYPE_11); | |
IRrecv irrecv(PIN_IR); | |
NeoSWSerial gpsSerial(PIN_GPS_RX, PIN_GPS_TX); | |
TinyGPS gps; | |
BMx280I2C bmp280(I2C_BMP280); | |
bool disp_on = false; | |
char disp_mode = 0; | |
unsigned long disp_last_button = UNKNOWN_ULONG; | |
#pragma GCC cold | |
void setup() { | |
#if DEBUG | |
Serial.begin(115200); | |
while (!Serial); | |
Serial.println(F("\n\n\n==============\nWeatherStation\n==============")); | |
#endif | |
lcd.begin(16, 2); | |
loadCustomChars(); | |
pinMode(PIN_LCD_BCKLGT, OUTPUT); | |
digitalWrite(PIN_LCD_BCKLGT, HIGH); | |
disp_on = true; | |
irrecv.enableIRIn(); | |
gpsSerial.begin(9600); | |
Wire.begin(); | |
if (!bmp280.begin()) { | |
#if DEBUG | |
Serial.println(F("BMP280 connection failed")); | |
#endif | |
} else { | |
bmp280.resetToDefaults(); | |
bmp280.writeOversamplingPressure(BMx280MI::OSRS_P_x16); | |
bmp280.writeOversamplingTemperature(BMx280MI::OSRS_T_x02); | |
bmp280.writeFilterSetting(BMx280MI::FILTER_x16); | |
bmp280.writeStandbyTime(BMx280MI::T_SB_7); | |
#if DEBUG | |
if (bmp280.readOversamplingTemperature() != BMx280MI::OSRS_T_x02) | |
Serial.println(F("BMP280 failed setting temperature oversampling")); | |
if (bmp280.readOversamplingPressure() != BMx280MI::OSRS_P_x16) | |
Serial.println(F("BMP280 failed setting pressure oversampling")); | |
if (bmp280.readFilterSetting() != BMx280MI::FILTER_x16) | |
Serial.println(F("BMP280 failed setting filter")); | |
if (bmp280.readStandbyTime() != BMx280MI::T_SB_7) | |
Serial.println(F("BMP280 failed setting standby time")); | |
#endif | |
} | |
#if DEBUG | |
Serial.println(F("setup complete")); | |
#endif | |
} | |
#pragma GCC optimize("O2") | |
void loop() { | |
unsigned long m = millis(); | |
static float temp, humid; | |
bool ok = measure(&temp, &humid, m); | |
int disp_humid = UNKNOWN_INT; | |
if (ok) { | |
if (humid > 99) | |
disp_humid = 99; | |
else if (humid < 0) | |
disp_humid = 0; | |
else | |
disp_humid = int(humid); | |
} | |
int disp_temp = UNKNOWN_INT; | |
if (ok) { | |
if (temp > 99) | |
disp_temp = 99; | |
else if (temp < -9) | |
disp_temp = -9; | |
else | |
disp_temp = int(temp); | |
} | |
int disp_pressure = UNKNOWN_INT; | |
if (bmp280.measure() && bmp280.hasValue()) { | |
float temperature = bmp280.getTemperature(); | |
float pressure = bmp280.getPressure64(); // Pa | |
float sealevel_pressure = sealevel(pressure, ALTITUDE); // Pa | |
disp_pressure = roundf(sealevel_pressure / 100.0); // hPa | |
#if DEBUG | |
Serial.print(F("BMP280 > ")); | |
Serial.print(pressure); | |
Serial.print(F(" ")); | |
Serial.print(sealevel_pressure); | |
Serial.print(F(" ")); | |
Serial.println(temperature); | |
#endif | |
} | |
static long gpstime = -1; | |
static unsigned short gpssat = -1; | |
static long gpslat = -1; | |
static long gpslon = -1; | |
if (gpsSerial.available()) { | |
if (gps.encode(gpsSerial.read())) { | |
byte hours, minutes, seconds; | |
gps.crack_datetime(NULL, NULL, NULL, &hours, &minutes, &seconds, NULL, NULL); | |
hours = (hours + TIMEZONE) % 24; | |
gpstime = (unsigned long)(hours) * 3600 + (unsigned long)(minutes) * 60 + (unsigned long)(seconds); | |
gpssat = gps.satellites(); | |
gps.get_position(&gpslat, &gpslon); | |
#if DEBUG | |
Serial.print(F("NEO-6M > ")); | |
Serial.print(gpstime); Serial.print(F(" ")); | |
Serial.print(gpslat); Serial.print(F(" ")); | |
Serial.print(gpslon); Serial.print(F(" ")); | |
Serial.println(gpssat); | |
#endif | |
} | |
} | |
bool refresh = false; | |
decode_results results; | |
if (irrecv.decode(&results)) { | |
refresh = parseCommand(results.value); | |
irrecv.resume(); | |
EEPROM.update(1, disp_mode); | |
} | |
if (disp_mode == 0) { | |
disp_mode = EEPROM.read(1); | |
if (disp_mode == 0) | |
disp_mode = 1; | |
refresh = true; | |
} | |
if (!disp_on) { | |
return; | |
} | |
if (refresh) { | |
lcd.clear(); | |
} | |
switch (disp_mode) { | |
case 1: draw1(refresh, disp_temp, disp_humid, disp_pressure, gpstime); break; | |
case 2: draw2(); break; | |
case 3: draw3(refresh, false, gpslat, gpslon, gpssat); break; | |
case 4: draw3(refresh, true, gpslat, gpslon, gpssat); break; | |
} | |
} | |
static bool measure(float *temp, float *humid, unsigned long now) { | |
static unsigned long last_measure = -60000UL; | |
if (dht.measure(temp, humid)) { | |
last_measure = now; | |
#if DEBUG | |
Serial.print(F("DHT11 > ")); | |
Serial.print(*temp); | |
Serial.print(F(" ")); | |
Serial.println(*humid); | |
#endif | |
} | |
return now - last_measure < 60000UL; | |
} | |
float sealevel(float pressure, float altitude) { | |
return pressure / pow( 1.0 - altitude / 44330.0, 5.255 ); | |
} | |
static bool parseCommand(unsigned long cmd) { | |
if (cmd != 0xFFFFFFFFUL) | |
disp_last_button = cmd; | |
int prev_disp_mode = disp_mode; | |
switch (cmd) { | |
case 0xFF30CF: // 1 | |
disp_mode = 1; | |
return disp_mode != prev_disp_mode; | |
case 0xFF18E7: // 2 | |
disp_mode = 2; | |
return disp_mode != prev_disp_mode; | |
case 0xFF7A85: // 3 | |
disp_mode = 3; | |
return disp_mode != prev_disp_mode; | |
case 0xFF10EF: // 4 | |
disp_mode = 4; | |
return disp_mode != prev_disp_mode; | |
case 0xFF38C7: // 5 | |
disp_mode = 5; | |
return disp_mode != prev_disp_mode; | |
case 0xFFA25D: // on/off | |
if (disp_on) { | |
lcd.noDisplay(); | |
digitalWrite(PIN_LCD_BCKLGT, LOW); | |
disp_on = false; | |
} else { | |
lcd.display(); | |
digitalWrite(PIN_LCD_BCKLGT, HIGH); | |
disp_on = true; | |
} | |
return disp_on; | |
default: | |
return false; | |
} | |
} | |
static void draw1(bool force, int temp, int humid, int pressure, unsigned long now) { | |
static int prev_temp = UNKNOWN_INT; | |
static int prev_humid = UNKNOWN_INT; | |
static int prev_pressure = UNKNOWN_INT; | |
static unsigned long prev_time = UNKNOWN_ULONG; | |
if (pressure == UNKNOWN_INT) pressure = prev_pressure; | |
bool ref1 = false, ref2 = false; | |
if (now != prev_time) ref2 = true; | |
if (pressure != prev_pressure) ref2 = true; | |
if (temp != prev_temp) ref1 = true; | |
if (humid != prev_humid) ref1 = true; | |
if (force) ref1 = ref2 = true; | |
refresh: | |
prev_temp = temp; | |
prev_humid = humid; | |
prev_pressure = pressure; | |
prev_time = now; | |
if (ref1) { | |
if (temp != UNKNOWN_INT) | |
disp_print(0, 0, PSTR("%2d\xDF""C %2d%% CO\1\2\4\4\3"), temp, humid); | |
else | |
disp_print(0, 0, PSTR("\2\3\xDF""C \2\3%% CO\1\2\4\4\3")); | |
} | |
if (ref2) { | |
if (now == -1 && pressure == UNKNOWN_INT) | |
disp_print(0, 1, PSTR("\2\4\4\3hPa \2\3:\2\3:\2\3")); | |
else if (pressure == UNKNOWN_INT) | |
disp_print(0, 1, PSTR("\2\4\4\3hPa %02d:%02d:%02d"), int((now / 60 / 60) % 60), int((now / 60) % 60), int((now) % 60)); | |
else if (now == -1) | |
disp_print(0, 1, PSTR("%4dhPa \2\3:\2\3:\2\3"), pressure); | |
else | |
disp_print(0, 1, PSTR("%4dhPa %02d:%02d:%02d"), pressure, int((now / 60 / 60) % 60), int((now / 60) % 60), int((now) % 60)); | |
} | |
} | |
static void draw2() { | |
disp_print(0, 0, PSTR("IR code %08lX"), disp_last_button); | |
} | |
static void draw3(bool force, bool alt, long gpslat, long gpslon, unsigned short gpssat) { | |
static long prev_gpslat = 0; | |
static long prev_gpslon = 0; | |
static unsigned short prev_gpssat = 0; | |
bool ref1 = false, ref2 = false; | |
if (gpslat != prev_gpslat) ref1 = true; | |
if (gpslon != prev_gpslon) ref2 = true; | |
if (gpssat != prev_gpssat) ref2 = true; | |
if (force) ref1 = ref2 = true; | |
prev_gpslat = gpslat; | |
prev_gpslon = gpslon; | |
prev_gpssat = gpssat; | |
if (!alt) { | |
if (ref1) disp_print(0, 0, PSTR("GPS %4ld.%06ld%c"), (gpslat / 1000000), ((gpslat >= 0 ? gpslat : -gpslat) % 1000000), (gpslat >= 0 ? 'N' : 'S')); | |
if (ref2) disp_print(0, 1, PSTR("\5%2d %4ld.%06ld%c"), gpssat > 99 ? 99 : gpssat, (gpslon / 1000000), ((gpslon >= 0 ? gpslon : -gpslon) % 1000000), (gpslon >= 0 ? 'E' : 'W')); | |
} else { | |
if (ref1) { | |
int gpslatdeg, gpslatmin, gpslatsec, gpslatmsec; | |
bool gpslatneg; | |
gpsdec2deg(gpslat, &gpslatdeg, &gpslatmin, &gpslatsec, &gpslatmsec, &gpslatneg); | |
disp_print(0, 0, PSTR("%1d %3d\xDF%02d'%02d\"%03d%c"), gpssat > 9 ? 9 : gpssat, gpslatdeg, gpslatmin, gpslatsec, gpslatmsec, (gpslatneg ? 'S' : 'N')); | |
} | |
if (ref2) { | |
int gpslondeg, gpslonmin, gpslonsec, gpslonmsec; | |
bool gpslonneg; | |
gpsdec2deg(gpslon, &gpslondeg, &gpslonmin, &gpslonsec, &gpslonmsec, &gpslonneg); | |
disp_print(0, 1, PSTR("\5 %3d\xDF%02d'%02d\"%03d%c"), gpslondeg, gpslonmin, gpslonsec, gpslonmsec, (gpslonneg ? 'W' : 'E')); | |
} | |
} | |
} | |
static void gpsdec2deg(long dec, int *deg, int *min, int *sec, int *msec, bool *neg) { | |
if (neg != NULL) *neg = dec < 0; | |
if (dec < 0) dec = -dec; | |
long _sec = dec * 9 / 2500; // * 3600 / 1000000 | |
if (msec != NULL) *msec = ((dec * 9) - (_sec * 2500)) * 1000 / 2500; | |
if (deg != NULL) *deg = _sec / 3600; | |
if (min != NULL) *min = (_sec / 60) % 60; | |
if (sec != NULL) *sec = _sec % 60; | |
} | |
static void disp_print(int col, int row, const char* fmt, ...) { | |
char data[16 - col + 1]; | |
va_list args; | |
va_start(args, fmt); | |
vsnprintf_P(data, sizeof(data), fmt, args); | |
va_end(args); | |
lcd.setCursor(col, row); | |
lcd.print(data); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment