Skip to content

Instantly share code, notes, and snippets.

@rpj
Created March 25, 2019 09:45
Show Gist options
  • Save rpj/0df1d464ce3200f529435a28c914b4e3 to your computer and use it in GitHub Desktop.
Save rpj/0df1d464ce3200f529435a28c914b4e3 to your computer and use it in GitHub Desktop.
#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