Created
March 25, 2019 09:45
-
-
Save rpj/0df1d464ce3200f529435a28c914b4e3 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
#define DEBUG 0 | |
// this sketch will build for the ESP8266 or ESP32 platform | |
#ifdef HAL_ESP32_HAL_H_ // ESP32 | |
#include <WiFiClient.h> | |
#include <WiFi.h> | |
#define LED_BLTIN_H LOW | |
#define LED_BLTIN_L HIGH | |
#else | |
#ifdef CORE_ESP8266_FEATURES_H // ESP8266 | |
#include <ESP8266WiFi.h> | |
#define LED_BLTIN_H HIGH | |
#define LED_BLTIN_L LOW | |
#endif | |
#endif | |
#include <Redis.h> | |
#include <TM1637Display.h> | |
#include <ArduinoJson.h> | |
// SET THESE | |
#define HOSTNAME "" | |
#define WIFI_SSID "" | |
#define WIFI_PASS "" | |
#define REDIS_IP "" | |
#define REDIS_PORT 0 | |
#define REDIS_PASS "" | |
// SET THESE if necessary | |
#define LED_BLTIN 2 | |
#define SER_BAUD 115200 | |
#define DEF_BRIGHT 0 | |
#if DEBUG | |
#define DEF_REFRESH 20 | |
#else | |
#define DEF_REFRESH 240 | |
#endif | |
#define dprint(fmt, ...) do { if (DEBUG) Serial.printf(fmt, ##__VA_ARGS__); } while (0) | |
#define EXEC_ALL_DISPS(EXEC_ME) do { \ | |
for (DisplaySpec* walk = gDisplays; walk->clockPin != -1 && walk->dioPin != -1; walk++) walk->disp->EXEC_ME; \ | |
} while(0) | |
struct Config { | |
int brightness; | |
int refresh; | |
}; | |
Config gConfig = { .brightness = DEF_BRIGHT, .refresh = DEF_REFRESH }; | |
struct RedisClientConn { | |
Redis* redis; | |
WiFiClient* conn; | |
}; | |
class DisplaySpec; | |
struct InfoSpec { | |
const char* listKey; | |
int startIdx; | |
int endIdx; | |
double lastTs; | |
int lastVal; | |
std::function<int(int)> adjFunc; | |
std::function<void(DisplaySpec*)> dispFunc; | |
}; | |
struct DisplaySpec { | |
int clockPin; | |
int dioPin; | |
TM1637Display* disp; | |
InfoSpec spec; | |
}; | |
int noop(int a) { return a; } | |
void d_def(DisplaySpec* d) { d->disp->showNumberDec(d->spec.lastVal); } | |
static uint8_t degFSegs[] = { 99, 113 }; | |
void d_tempf(DisplaySpec* d) { | |
d->disp->showNumberDecEx(d->spec.lastVal, 0, false); | |
d->disp->setSegments(degFSegs, 2, 2); | |
} | |
RedisClientConn g_RedisConn; | |
DisplaySpec gDisplays[] = { | |
{ 33, 32, nullptr, | |
{ "zero:sensor:BME280:temperature:.list", 0, 11, 0.0, 0, noop, d_tempf } }, | |
{ 26, 25, nullptr, | |
{ "zero:sensor:DHTXX:temperature_fahrenheit:.list", 0, 11, 0.0, 0, noop, d_tempf } }, | |
{ 18, 19, nullptr, | |
{ "zero:sensor:BME280:pressure:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def } }, | |
{ 12, 14, nullptr, | |
{ "zed:sensor:SPS30:mc_2p5:.list", 0, 5, 0.0, 0, [](int i) { return i / 100; }, d_def } }, | |
{ -1, -1, nullptr, | |
{ nullptr, -1, -1, -1.0, -1, noop, d_def } } | |
}; | |
void blink(int d = 50) { | |
digitalWrite(LED_BLTIN, LED_BLTIN_H); | |
delay(d); | |
digitalWrite(LED_BLTIN, LED_BLTIN_L); | |
delay(d); | |
digitalWrite(LED_BLTIN, LED_BLTIN_H); | |
} | |
struct AnimStep { | |
int digit; | |
int bits; | |
}; | |
AnimStep full_loop[] = { {0, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 3}, {3, 7}, | |
{3, 15}, {2, 9}, {1, 9}, {0, 9}, {0, 25}, {0, 57}, {-1, -1} }; | |
AnimStep light_loop[] = { {0, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 2}, {3, 4}, | |
{3, 8}, {2, 8}, {1, 8}, {0, 8}, {0, 16}, {0, 32}, {-1, -1} }; | |
void run_animation(TM1637Display* d, AnimStep* anim, bool cE = false, int s = 0) { | |
uint8_t v[] = {0, 0, 0, 0}; | |
for (AnimStep* w = anim; w->bits != -1 && w->digit != -1; w++) { | |
if (cE) bzero(v, 4); | |
v[w->digit] = w->bits; | |
d->setSegments(v); | |
if (s) delay(s); | |
} | |
} | |
bool tmdisplay_init() { | |
Serial.printf("Initializing displays with brightness level %d\n", gConfig.brightness); | |
DisplaySpec* spec = gDisplays; | |
for (; spec->clockPin != -1 && spec->dioPin != -1; spec++) { | |
Serial.printf("Setting up display #%d with clock=%d DIO=%d\n", | |
(int)(spec - gDisplays), spec->clockPin, spec->dioPin); | |
spec->disp = new TM1637Display(spec->clockPin, spec->dioPin); | |
spec->disp->clear(); | |
spec->disp->setBrightness(gConfig.brightness); | |
run_animation(spec->disp, full_loop, false, 5); | |
} | |
#if DEBUG | |
EXEC_ALL_DISPS(showNumberDecEx((int)(walk - gDisplays), 0, false)); | |
delay(2000); | |
#endif | |
return true; | |
} | |
bool wifi_init() { | |
auto bstat = WiFi.begin(WIFI_SSID, WIFI_PASS); | |
dprint("Connecting to to '%s'...\n", WIFI_SSID); | |
dprint("WiFi.begin() -> %d\n", bstat); | |
run_animation(gDisplays[0].disp, light_loop, true); | |
gDisplays[0].disp->clear(); | |
gDisplays[0].disp->showNumberHexEx(0xffff, 64, true); | |
// TODO: timeout! | |
int _c = 0; | |
while (WiFi.status() != WL_CONNECTED) { | |
gDisplays[0].disp->showNumberHexEx((++_c << 8) + 0xff, 64, true); | |
} | |
Serial.printf("Connected to '%s' as %s\n", WIFI_SSID, WiFi.localIP().toString().c_str()); | |
gDisplays[0].disp->showNumberHexEx(0xFF00 | WiFi.status(), 64, false); | |
return true; | |
} | |
RedisClientConn redis_init() { | |
RedisClientConn rv; | |
rv.conn = new WiFiClient(); | |
if (!rv.conn->connect(REDIS_IP, REDIS_PORT)) { | |
dprint("Redis connection failed"); | |
delete rv.conn, rv.conn = nullptr; | |
} | |
else { | |
rv.redis = new Redis(*rv.conn); | |
if (rv.redis->authenticate(REDIS_PASS) != RedisSuccess) { | |
dprint("Redis auth failed"); | |
delete rv.redis, rv.redis = nullptr; | |
} | |
else { | |
auto ipStr = WiFi.localIP().toString(); | |
rv.redis->set(HOSTNAME ":info:ip", ipStr.c_str()); | |
} | |
} | |
return rv; | |
} | |
StaticJsonBuffer<256> jsonBuf; | |
void updateDisplay(DisplaySpec* disp) { | |
run_animation(disp->disp, full_loop); | |
auto lrVec = g_RedisConn.redis->lrange(disp->spec.listKey, disp->spec.startIdx, disp->spec.endIdx); | |
dprint("'%s'! %d elements\n", disp->spec.listKey, lrVec.size()); | |
if (lrVec.size()) { | |
double acc = 0.0; | |
for (auto lrStr : lrVec) { | |
if (lrStr.length() < 256) { | |
jsonBuf.clear(); | |
JsonArray& jsRoot = jsonBuf.parseArray(lrStr.c_str()); | |
disp->spec.lastTs = (double)jsRoot[0]; | |
acc += (double)jsRoot[1]; | |
} | |
} | |
run_animation(disp->disp, light_loop, true); | |
disp->spec.lastVal = disp->spec.adjFunc((int)((acc * 100.0) / lrVec.size())); | |
disp->spec.dispFunc(disp); | |
dprint("'%s': LTS %f ACC %f IVAL %d\n", disp->spec.listKey, disp->spec.lastTs, acc, disp->spec.lastVal); | |
} | |
} | |
void readRedisConfig() { | |
auto bc = g_RedisConn.redis->get(HOSTNAME ":config:brightness"); | |
auto rc = g_RedisConn.redis->get(HOSTNAME ":config:refresh"); | |
auto bcInt = bc.toInt(); | |
auto rcInt = rc.toInt(); | |
if (bcInt >= 0 && bcInt < 8 && bcInt != gConfig.brightness) { | |
Serial.printf("Read brightness level: %d\n", bcInt); | |
gConfig.brightness = bcInt; | |
EXEC_ALL_DISPS(setBrightness(gConfig.brightness)); | |
} | |
if (rcInt >= 5 && rcInt != gConfig.refresh) { | |
Serial.printf("Read refresh rate: %d\n", rcInt); | |
gConfig.refresh = rcInt; | |
} | |
} | |
void setup() { | |
pinMode(LED_BLTIN, OUTPUT); | |
Serial.begin(SER_BAUD); | |
Serial.println("Initializing..."); | |
if (tmdisplay_init() && wifi_init()) { | |
g_RedisConn = redis_init(); | |
if (g_RedisConn.redis) { | |
Serial.println("Initialized!"); | |
} | |
} | |
} | |
int __lc = 0; | |
int _last_free = 0; | |
void loop() { | |
if (!(__lc++ % gConfig.refresh)) { | |
readRedisConfig(); | |
auto sfree = String(_last_free); | |
for (DisplaySpec* w = gDisplays; w->clockPin != -1 && w->dioPin != -1; w++) | |
updateDisplay(w); | |
auto ut = String(millis() / 1000); | |
(void)g_RedisConn.redis->set(HOSTNAME ":uptime", ut.c_str()); | |
#if DEBUG | |
int cur_free = ESP.getFreeHeap(); | |
dprint("CURFREE %d LASTFREE %d DELTA %d\n", cur_free, _last_free, cur_free - _last_free); | |
_last_free = cur_free; | |
#endif | |
} | |
if (!(__lc % 5)) { blink(15); delay(5); blink(15); } | |
dprint("%c%s", !(__lc % 5) ? '|' : '.', __lc % gConfig.refresh ? "" : "\n"); | |
delay(900); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment