Initial commit
This commit is contained in:
34
src/Battery.cpp
Normal file
34
src/Battery.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// FILE: Battery.h
|
||||
// VERSION: 1.0
|
||||
// PURPOSE: Reads battery voltage and returns as a percentage value
|
||||
//
|
||||
//
|
||||
|
||||
#include "Battery.h"
|
||||
#include "mgos.h"
|
||||
#include "mgos_adc.h"
|
||||
|
||||
#ifdef ESP8266
|
||||
extern "C" {
|
||||
#include "user_interface.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
#define RefVoltage_mV 3200
|
||||
#define MaxValueToBeRead 1023
|
||||
|
||||
Battery::Battery() {
|
||||
mgos_adc_enable(0);
|
||||
}
|
||||
|
||||
int Battery::getPercentage() {
|
||||
int value = mgos_adc_read(0);
|
||||
return (value * 100) / MaxValueToBeRead;
|
||||
}
|
||||
|
||||
int Battery::getVoltage_mV() {
|
||||
int value = mgos_adc_read(0);
|
||||
return RefVoltage_mV * value / MaxValueToBeRead;
|
||||
}
|
||||
|
||||
20
src/Battery.h
Normal file
20
src/Battery.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// FILE: DjBattery.h
|
||||
// VERSION: 1.0
|
||||
// PURPOSE: Reads battery voltage
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef _batteryLoaded
|
||||
#define _batteryLoaded true
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class Battery {
|
||||
public:
|
||||
Battery();
|
||||
int getPercentage();
|
||||
int getVoltage_mV();
|
||||
};
|
||||
|
||||
#endif
|
||||
370
src/ClockClient.cpp
Normal file
370
src/ClockClient.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
//
|
||||
// FILE: ClockClient.cpp
|
||||
// PURPOSE: UDP broadcast listener for fastclock (FREMO clock)
|
||||
//
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <lwip/igmp.h>
|
||||
|
||||
#include "common/platform.h"
|
||||
#include "mgos.h"
|
||||
#include "mgos_app.h"
|
||||
#include "mongoose/mongoose.h"
|
||||
#include "mgos_wifi.h"
|
||||
#include "mgos_net.h"
|
||||
#include "mgos_mongoose.h"
|
||||
#include "mgos_sys_config.h"
|
||||
#include "ClockClient.h"
|
||||
|
||||
|
||||
#define CLOCK_PACKET_SIZE 1024
|
||||
static char packetBuffer[CLOCK_PACKET_SIZE+1]; //buffer to hold incoming and outgoing packets
|
||||
|
||||
const char * ClockClient::getLastMessage() { return (const char *) packetBuffer; }
|
||||
|
||||
int ClockClient::protocolVersion{0};
|
||||
char * ClockClient::listenToName{nullptr};
|
||||
char * ClockClient::name{nullptr};
|
||||
char * ClockClient::text{nullptr};
|
||||
char * ClockClient::clocktype{nullptr};
|
||||
bool ClockClient::active{false};
|
||||
bool ClockClient::isFastclock{false};
|
||||
double ClockClient::speed{1.0};
|
||||
char * ClockClient::clock{nullptr};
|
||||
unsigned int ClockClient::weekday{0};
|
||||
unsigned int ClockClient::clockHours{0};
|
||||
unsigned int ClockClient::clockMinutes{0};
|
||||
unsigned int ClockClient::clockSeconds{0};
|
||||
int ClockClient::numClockChangeCallbacks{0};
|
||||
char * ClockClient::fastclockIP{nullptr};
|
||||
unsigned int ClockClient::fastclockPort{0};
|
||||
unsigned int ClockClient::listenPort{0};
|
||||
|
||||
ClockChangeCallback ClockClient::clockChangeCallback[];
|
||||
|
||||
static void strFree(char **target) {
|
||||
if (*target != nullptr) { free(*target); *target = nullptr; }
|
||||
}
|
||||
|
||||
static void strReplace(char **target, const char *newValue) {
|
||||
strFree(target);
|
||||
*target = strdup(newValue);
|
||||
}
|
||||
|
||||
void ClockClient::addClockChangeCallback(ClockChangeCallback _clockChangeCallback) {
|
||||
if (numClockChangeCallbacks >= MAX_CLOCK_CHANGE_CALLBACKS) {
|
||||
LOG(LL_ERROR, ("ERROR: Too many clock change callbacks registered!"));
|
||||
return;
|
||||
}
|
||||
clockChangeCallback[numClockChangeCallbacks++] = _clockChangeCallback;
|
||||
}
|
||||
|
||||
ClockClient::ClockClient() {
|
||||
fastclockScanner = FastclockScanner();
|
||||
}
|
||||
|
||||
static void multicastMessageHandler(struct mg_connection *nc, int ev, void *p) {
|
||||
struct mbuf *io = &nc->recv_mbuf;
|
||||
(void) p;
|
||||
|
||||
switch (ev) {
|
||||
case MG_EV_RECV:
|
||||
LOG(LL_INFO, ("Received (%d bytes): '%.*s'", (int) io->len, (int) io->len, io->buf));
|
||||
ClockClient::interpretClockMessage(io->len, io->buf);
|
||||
mbuf_remove(io, io->len);
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_wifi_change(enum mgos_wifi_status event, void *ud) {
|
||||
(void) ud;
|
||||
|
||||
switch (event) {
|
||||
case MGOS_WIFI_IP_ACQUIRED: {
|
||||
ip_addr_t host_addr;
|
||||
ip_addr_t group_addr;
|
||||
struct mgos_net_ip_info ip_info;
|
||||
char sta_ip[16];
|
||||
|
||||
if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_WIFI, MGOS_NET_IF_WIFI_STA, &ip_info)) {
|
||||
mgos_net_ip_to_str(&ip_info.ip, sta_ip);
|
||||
}
|
||||
host_addr.addr = inet_addr(sta_ip);
|
||||
group_addr.addr = inet_addr(ClockClient::getMulticastIP());
|
||||
|
||||
LOG(LL_INFO, ("Joining multicast group %s", ClockClient::getMulticastIP()));
|
||||
|
||||
if (igmp_joingroup(&host_addr, &group_addr) != ERR_OK) {
|
||||
LOG(LL_INFO, ("udp_join_multigroup failed!"));
|
||||
};
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static int init_multicastListener(struct mg_mgr *mgr, char *multicast, int port) {
|
||||
struct mg_bind_opts bopts;
|
||||
char listener_spec[128];
|
||||
(void) multicast;
|
||||
|
||||
snprintf(listener_spec, sizeof(listener_spec), "udp://:%d", port);
|
||||
LOG(LL_INFO, ("Listening on %s", listener_spec));
|
||||
|
||||
memset(&bopts, 0, sizeof(bopts));
|
||||
struct mg_connection *lc = mg_bind_opt(mgr, listener_spec, multicastMessageHandler, bopts);
|
||||
if (lc == NULL) {
|
||||
LOG(LL_ERROR, ("Failed to create listener"));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ClockClient::begin() {
|
||||
strReplace(&name, mgos_sys_config_get_clock_listenToName());
|
||||
// WiFi.mode(WIFI_STA);
|
||||
strReplace(&multicast, mgos_sys_config_get_clock_multicast());
|
||||
listenPort = mgos_sys_config_get_clock_port();
|
||||
logHeap();
|
||||
|
||||
|
||||
mgos_wifi_add_on_change_cb(on_wifi_change, NULL);
|
||||
if (!init_multicastListener(mgos_get_mgr(), multicast, listenPort)) {
|
||||
LOG(LL_ERROR, ("Starting listener for Fastclock Multicast failed"));
|
||||
}
|
||||
}
|
||||
|
||||
void ClockClient::setDefaults() {
|
||||
// do not touch listenToName, multicast, listenPort
|
||||
// strFree(&listenToName);
|
||||
// listenPort = 0;
|
||||
// strFree(&multicast);
|
||||
protocolVersion = 0;
|
||||
active = false;
|
||||
isFastclock = false;
|
||||
speed = 0;
|
||||
strReplace(&name, "DefaultClock");
|
||||
strReplace(&text, "");
|
||||
weekday = 0;
|
||||
clockHours = 0;
|
||||
clockMinutes = 0;
|
||||
clockSeconds = 0;
|
||||
strFree(&fastclockIP);
|
||||
fastclockPort = 2000;
|
||||
}
|
||||
|
||||
static const char *weekdayName[] = {
|
||||
"---",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday"
|
||||
};
|
||||
|
||||
const char* ClockClient::getClockWeekdayName() {
|
||||
if (weekday > sizeof(weekdayName)) {
|
||||
LOG(LL_ERROR, ("Invalid weekday %d, have no name for this", weekday));
|
||||
return weekdayName[0];
|
||||
}
|
||||
return weekdayName[weekday];
|
||||
}
|
||||
|
||||
static int movePointerForwardUntilEndOfLine(char **source) {
|
||||
int moved = 0;
|
||||
while (**source != '\r' && **source != '\n' && **source != '\0') {
|
||||
(*source)++;
|
||||
++moved;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
#define MAX_LINE_LENGTH 512
|
||||
static int copyUntilEndOfLine(const char* parameter, const char* source, char** target) {
|
||||
char buffer[MAX_LINE_LENGTH+1];
|
||||
char* buffer_ptr;
|
||||
int copied=0;
|
||||
|
||||
buffer_ptr = &buffer[0];
|
||||
while (*source != '\r' && *source != '\n' && *source != '\0' && copied < MAX_LINE_LENGTH) {
|
||||
*buffer_ptr++ = *source++;
|
||||
copied++;
|
||||
}
|
||||
*buffer_ptr = '\0';
|
||||
|
||||
if (copied >= MAX_LINE_LENGTH) {
|
||||
if (*source != '\r' && *source != '\n' && *source != '\0') {
|
||||
LOG(LL_ERROR, ("String to copy is too long for parameter %s (rest: %s) -- ignored!", parameter, source));
|
||||
while (*source != '\r' && *source != '\n' && *source != '\0')
|
||||
++source; // skip
|
||||
}
|
||||
}
|
||||
|
||||
strReplace(target, buffer_ptr);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int copyBoolean(const char* parameter, const char* source, bool* target) {
|
||||
if (strncmp(source, "yes", 3) == 0) {
|
||||
*target = true;
|
||||
return 3;
|
||||
} else if (strncmp(source, "no", 2) == 0) {
|
||||
*target = false;
|
||||
return 2;
|
||||
}
|
||||
LOG(LL_ERROR, ("Invalid boolean value found for parameter %s (%s) -- ignored (using default=false)!", parameter, source));
|
||||
*target = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copyInteger(const char *source, unsigned int *result) {
|
||||
char *movedSource = (char *) source;
|
||||
|
||||
*result = (unsigned int) strtoul(movedSource, &movedSource, 10);
|
||||
movePointerForwardUntilEndOfLine(&movedSource);
|
||||
|
||||
return movedSource - source;
|
||||
}
|
||||
|
||||
static int copyDouble(const char *source, double *result) {
|
||||
char *movedSource = (char *) source;
|
||||
|
||||
*result = (double) strtod(movedSource, &movedSource);
|
||||
movePointerForwardUntilEndOfLine(&movedSource);
|
||||
|
||||
return movedSource - source;
|
||||
}
|
||||
|
||||
static int copyClock(const char *source, unsigned int *hours, unsigned int *minutes, unsigned int *seconds) {
|
||||
char *movedSource = (char *) source;
|
||||
*hours = 0;
|
||||
*minutes = 0;
|
||||
*seconds = 0;
|
||||
|
||||
*hours = strtoul(movedSource, &movedSource, 10);
|
||||
if (*movedSource != ':') {
|
||||
LOG(LL_ERROR, ("Invalid clock value found (%s) -- missing colon after hours!", source));
|
||||
} else {
|
||||
++movedSource;
|
||||
*minutes = strtoul(movedSource, &movedSource, 10);
|
||||
if (*movedSource != ':') {
|
||||
LOG(LL_ERROR, ("Invalid clock value found (%s) -- missing colon after minutes!", source));
|
||||
} else {
|
||||
++movedSource;
|
||||
*seconds = strtoul(movedSource, &movedSource, 10);
|
||||
}
|
||||
}
|
||||
movePointerForwardUntilEndOfLine(&movedSource);
|
||||
|
||||
return movedSource - source;
|
||||
}
|
||||
|
||||
void ClockClient::interpretClockMessage(int len, char* msg) {
|
||||
int copied = 0;
|
||||
|
||||
setDefaults();
|
||||
// ignore line 1, as this is for 1st version of clock clients only
|
||||
len -= movePointerForwardUntilEndOfLine(&msg);
|
||||
|
||||
while (len > 0 && *msg != '\0') {
|
||||
if (msg[0] == '\r' || msg[0] == '\n') {
|
||||
msg++;
|
||||
len--;
|
||||
} else if (strncmp(msg, "version=", 8) == 0) {
|
||||
if (msg[8] == '1') {
|
||||
protocolVersion = 1;
|
||||
} else if (msg[8] == '2') {
|
||||
protocolVersion = 2;
|
||||
} else {
|
||||
LOG(LL_ERROR, ("Invalid version in fastclock message (%c) -- ignored!", msg[8]));
|
||||
}
|
||||
msg += 9;
|
||||
len -= 9;
|
||||
} else if (strncmp(msg, "name=", 5) == 0) {
|
||||
msg += 5;
|
||||
len -= 5;
|
||||
copied = copyUntilEndOfLine("name", msg, &name);
|
||||
fastclockScanner.addClock(name);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "text=", 5) == 0) {
|
||||
msg += 5;
|
||||
len -= 5;
|
||||
copied = copyUntilEndOfLine("text", msg, &text);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "clock=", 6) == 0) {
|
||||
msg += 6;
|
||||
len -= 6;
|
||||
copied = copyClock(msg, &clockHours, &clockMinutes, &clockSeconds);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "active=", 7) == 0) {
|
||||
msg += 7;
|
||||
len -= 7;
|
||||
copied = copyBoolean("active", msg, &active);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "speed=", 6) == 0) {
|
||||
msg += 6;
|
||||
len -= 6;
|
||||
copied = copyDouble(msg, &speed);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "weekday=", 8) == 0) {
|
||||
msg += 8;
|
||||
len -= 8;
|
||||
copied = copyInteger(msg, &weekday);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "ip-port=", 8) == 0) {
|
||||
msg += 8;
|
||||
len -= 8;
|
||||
copied = copyInteger(msg, &fastclockPort);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "ip-address=", 11) == 0) {
|
||||
msg += 11;
|
||||
len -= 11;
|
||||
copied = copyUntilEndOfLine("ip-address", msg, &fastclockIP);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
} else if (strncmp(msg, "clocktype=", 10) == 0) {
|
||||
char * buffer = nullptr;
|
||||
msg += 10;
|
||||
len -= 10;
|
||||
copied = copyUntilEndOfLine("clocktype", msg, &buffer);
|
||||
msg += copied;
|
||||
len -= copied;
|
||||
if (strcmp(buffer, "fastclock") == 0) {
|
||||
isFastclock = true;
|
||||
} else if (strcmp(buffer, "realclock") == 0) {
|
||||
isFastclock = false;
|
||||
} else {
|
||||
LOG(LL_ERROR, ("Invalid value for parameter clocktype in fastclock message (%s) -- using default (fastclock)!", buffer));
|
||||
}
|
||||
strFree(&buffer);
|
||||
} else {
|
||||
LOG(LL_ERROR, ("Invalid parameter in fastclock message (%s) -- ignored!", msg));
|
||||
len -= movePointerForwardUntilEndOfLine(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(name, listenToName) == 0) {
|
||||
// yes, we follow this clock, so do not ignore
|
||||
// propagate new message
|
||||
for (int i=0; i<numClockChangeCallbacks; ++i) {
|
||||
clockChangeCallback[i](clockHours, clockMinutes, clockSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClockClient::loop() {
|
||||
}
|
||||
77
src/ClockClient.h
Normal file
77
src/ClockClient.h
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// FILE: ClockClient.h
|
||||
// VERSION: 0.1
|
||||
// PURPOSE: FREMO Clock Client
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef _clockClientLoaded
|
||||
#define _clockClientLoaded
|
||||
|
||||
#include <appDebug.h>
|
||||
#include <FastclockScanner.h>
|
||||
|
||||
#define MAX_CLOCK_CHANGE_CALLBACKS 5
|
||||
|
||||
|
||||
typedef void (*ClockChangeCallback)(int h, int m, int s);
|
||||
|
||||
class ClockClient
|
||||
{
|
||||
public:
|
||||
ClockClient();
|
||||
static void begin();
|
||||
static void loop();
|
||||
static void setListenToClock(const char *_name) { listenToName = strdup(_name); }
|
||||
static const char * getLastMessage();
|
||||
static int getProtocolVersion() { return protocolVersion; }
|
||||
static const char * getText() { return text; }
|
||||
static const char * getClock() { return clock; }
|
||||
static const char * getName() { return name; }
|
||||
static bool isActive() { return active; }
|
||||
static float getSpeed() { return speed; }
|
||||
static unsigned int getClockHours() { return clockHours; }
|
||||
static unsigned int getClockMinutes() { return clockMinutes; }
|
||||
static unsigned int getClockSeconds() { return clockSeconds; }
|
||||
static unsigned int getClockWeekday() { return weekday; }
|
||||
static const char * getClockWeekdayName();
|
||||
static void addClockChangeCallback(ClockChangeCallback callback);
|
||||
static int getNumberOfKnownClocks();
|
||||
static const char ** getKnownClocks();
|
||||
static unsigned int getListenPort() { return listenPort; }
|
||||
static char * getMulticastIP() { return multicast; }
|
||||
static char * getFastclockServerIP() { return fastclockIP; }
|
||||
static unsigned int getFastclockServerPort() { return fastclockPort; }
|
||||
static char * getClockString() {
|
||||
static char output[9];
|
||||
snprintf(output, sizeof(output), "%02d:%02d:%02d", clockHours, clockMinutes, clockSeconds);
|
||||
return output;
|
||||
}
|
||||
static void setDefaults();
|
||||
static void interpretClockMessage(int len, char* msg);
|
||||
|
||||
|
||||
private:
|
||||
static FastclockScanner fastclockScanner;
|
||||
static int numClockChangeCallbacks;
|
||||
static ClockChangeCallback clockChangeCallback[MAX_CLOCK_CHANGE_CALLBACKS];
|
||||
static int protocolVersion;
|
||||
static char * listenToName;
|
||||
static char * name;
|
||||
static char * text;
|
||||
static char * clocktype;
|
||||
static bool active;
|
||||
static bool isFastclock; // false --> real clock
|
||||
static double speed;
|
||||
static char * clock;
|
||||
static unsigned int clockHours;
|
||||
static unsigned int clockMinutes;
|
||||
static unsigned int clockSeconds;
|
||||
static unsigned int weekday;
|
||||
static char * fastclockIP;
|
||||
static unsigned int fastclockPort;
|
||||
static unsigned int listenPort;
|
||||
static char * multicast;
|
||||
};
|
||||
|
||||
#endif
|
||||
30
src/FastclockScanner.cpp
Normal file
30
src/FastclockScanner.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// FILE: FastclockScanner.h
|
||||
// VERSION: 1.0
|
||||
// PURPOSE: Scans the broadcasts for clocks and returns the clocknames found
|
||||
//
|
||||
//
|
||||
|
||||
#include <string.h>
|
||||
#include "mgos.h"
|
||||
#include "FastclockScanner.h"
|
||||
#include "appDebug.h"
|
||||
|
||||
|
||||
const char *FastclockScanner::knownClocks[MAX_CLOCKS];
|
||||
int FastclockScanner::numberOfKnownClocks = 0;
|
||||
|
||||
void FastclockScanner::addClock(const char *clockName) {
|
||||
for (int i=0; i<numberOfKnownClocks; ++i) {
|
||||
if (strcmp(clockName, knownClocks[i]) == 0) return;
|
||||
}
|
||||
if (numberOfKnownClocks < MAX_CLOCKS) {
|
||||
knownClocks[numberOfKnownClocks] = strdup(clockName);
|
||||
++numberOfKnownClocks;
|
||||
LOG(LL_DEBUG, ("Added new clock with name=%s", clockName));
|
||||
}
|
||||
logHeap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
22
src/FastclockScanner.h
Normal file
22
src/FastclockScanner.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* FastclockScanner -- collect fastclock server names
|
||||
*/
|
||||
|
||||
#ifndef _FastclockScannerIncluded
|
||||
#define _FastclockScannerIncluded
|
||||
|
||||
#include "appDebug.h"
|
||||
|
||||
#define MAX_CLOCKS 10
|
||||
|
||||
class FastclockScanner {
|
||||
public:
|
||||
FastclockScanner() {};
|
||||
void addClock(const char * clockName);
|
||||
const char **getKnownClocks() { return knownClocks; }
|
||||
int getNumberOfKnownClocks() { return numberOfKnownClocks; }
|
||||
private:
|
||||
static const char *knownClocks[];
|
||||
static int numberOfKnownClocks;
|
||||
};
|
||||
|
||||
#endif
|
||||
414
src/UI.cpp
Normal file
414
src/UI.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
/*********************************************************************
|
||||
UI.cpp
|
||||
Fastclock Client Display
|
||||
*********************************************************************/
|
||||
#define USE_I2C true
|
||||
#define USE_SPI false
|
||||
#define SSD1306_64_48
|
||||
|
||||
#include <stdio.h>
|
||||
#include "mgos.h"
|
||||
#include "common/platform.h"
|
||||
#include "common/cs_file.h"
|
||||
#include "mgos_app.h"
|
||||
#include "mgos_gpio.h"
|
||||
#include "mgos_sys_config.h"
|
||||
#include "mgos_timers.h"
|
||||
#include "mgos_hal.h"
|
||||
#include "mgos_dlsym.h"
|
||||
#include "mjs.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Adafruit_SSD1306dj.h"
|
||||
|
||||
#include "mgos_rpc.h"
|
||||
#include "common/cs_dbg.h"
|
||||
#include "common/json_utils.h"
|
||||
#include "frozen/frozen.h"
|
||||
#include "mgos_net.h"
|
||||
|
||||
#include "Ui.h"
|
||||
#include "fremo_logo.xbm"
|
||||
|
||||
// OLED display object
|
||||
Adafruit_SSD1306 display;
|
||||
|
||||
// Use these variables to set the initial time
|
||||
int hours = 11;
|
||||
int minutes = 50;
|
||||
int seconds = 30;
|
||||
|
||||
// How fast do you want the clock to spin? Set this to 1 for fun.
|
||||
// Set this to 1000 to get _about_ 1 second timing.
|
||||
// CLOCK_SPEED = # of milliseconds per model minute
|
||||
const int CLOCK_SPEED = 1000;
|
||||
|
||||
// Global variables to help draw the clock face:
|
||||
boolean showSecondsArm = true;
|
||||
int MIDDLE_Y, MIDDLE_X;
|
||||
int CLOCK_RADIUS;
|
||||
int POS_12_X, POS_12_Y;
|
||||
int POS_3_X, POS_3_Y;
|
||||
int POS_6_X, POS_6_Y;
|
||||
int POS_9_X, POS_9_Y;
|
||||
int S_LENGTH;
|
||||
int M_LENGTH;
|
||||
int H_LENGTH;
|
||||
int TEXT_WIDTH = 6;
|
||||
int TEXT_HEIGHT = 8;
|
||||
|
||||
unsigned long lastDraw = 0;
|
||||
|
||||
void initClockVariables()
|
||||
{
|
||||
// Calculate constants for clock face component positions:
|
||||
display.setFontType(1);
|
||||
MIDDLE_Y = display.height() / 2;
|
||||
MIDDLE_X = display.width() / 2;
|
||||
CLOCK_RADIUS = min(MIDDLE_X, MIDDLE_Y) - 1;
|
||||
POS_12_X = MIDDLE_X - TEXT_WIDTH;
|
||||
POS_12_Y = MIDDLE_Y - CLOCK_RADIUS + 2;
|
||||
POS_3_X = MIDDLE_X + CLOCK_RADIUS - TEXT_WIDTH - 1;
|
||||
POS_3_Y = MIDDLE_Y - TEXT_HEIGHT/2;
|
||||
POS_6_X = MIDDLE_X - TEXT_WIDTH/2;
|
||||
POS_6_Y = MIDDLE_Y + CLOCK_RADIUS - TEXT_HEIGHT - 1;
|
||||
POS_9_X = MIDDLE_X - CLOCK_RADIUS + TEXT_WIDTH - 2;
|
||||
POS_9_Y = MIDDLE_Y - TEXT_HEIGHT/2;
|
||||
|
||||
// Calculate clock arm lengths
|
||||
S_LENGTH = CLOCK_RADIUS * 0.1; // CLOCK_RADIUS - 2;
|
||||
M_LENGTH = CLOCK_RADIUS * 0.8;
|
||||
H_LENGTH = CLOCK_RADIUS * 0.5;
|
||||
}
|
||||
|
||||
|
||||
// Simple function to increment seconds and then increment minutes
|
||||
// and hours if necessary.
|
||||
void updateTime()
|
||||
{
|
||||
seconds++; // Increment seconds
|
||||
if (seconds >= 60) // If seconds overflows (>=60)
|
||||
{
|
||||
seconds = 0; // Set seconds back to 0
|
||||
minutes++; // Increment minutes
|
||||
if (minutes >= 60) // If minutes overflows (>=60)
|
||||
{
|
||||
minutes = 0; // Set minutes back to 0
|
||||
hours++; // Increment hours
|
||||
if (hours >= 12) // If hours overflows (>=12)
|
||||
{
|
||||
hours = 0; // Set hours back to 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to map value from one range to another
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max)
|
||||
{
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
// Draw the clock's three arms: seconds, minutes, hours.
|
||||
void drawArms(int h, int m, int s)
|
||||
{
|
||||
double midHours; // this will be used to slightly adjust the hour hand
|
||||
static int hx, hy, mx, my, sx, sy;
|
||||
|
||||
// Adjust time to shift display 90 degrees ccw
|
||||
// this will turn the clock the same direction as text:
|
||||
h -= 3;
|
||||
m -= 15;
|
||||
s -= 15;
|
||||
if (h <= 0)
|
||||
h += 12;
|
||||
if (m < 0)
|
||||
m += 60;
|
||||
if (s < 0)
|
||||
s += 60;
|
||||
|
||||
if (showSecondsArm) {
|
||||
// Calculate and draw new lines:
|
||||
s = map(s, 0, 60, 0, 360); // map the 0-60, to "360 degrees"
|
||||
sx = S_LENGTH * cos(PI * ((float)s) / 180); // woo trig!
|
||||
sy = S_LENGTH * sin(PI * ((float)s) / 180); // woo trig!
|
||||
// draw the second hand:
|
||||
display.drawLine(MIDDLE_X, MIDDLE_Y, MIDDLE_X + sx, MIDDLE_Y + sy, WHITE);
|
||||
}
|
||||
|
||||
m = map(m, 0, 60, 0, 360); // map the 0-60, to "360 degrees"
|
||||
mx = M_LENGTH * cos(PI * ((float)m) / 180); // woo trig!
|
||||
my = M_LENGTH * sin(PI * ((float)m) / 180); // woo trig!
|
||||
// draw the minute hand
|
||||
display.drawLine(MIDDLE_X, MIDDLE_Y, MIDDLE_X + mx, MIDDLE_Y + my, WHITE);
|
||||
|
||||
midHours = minutes/12; // midHours is used to set the hours hand to middling levels between whole hours
|
||||
h *= 5; // Get hours and midhours to the same scale
|
||||
h += midHours; // add hours and midhours
|
||||
h = map(h, 0, 60, 0, 360); // map the 0-60, to "360 degrees"
|
||||
hx = H_LENGTH * cos(PI * ((float)h) / 180); // woo trig!
|
||||
hy = H_LENGTH * sin(PI * ((float)h) / 180); // woo trig!
|
||||
// draw the hour hand:
|
||||
display.drawLine(MIDDLE_X, MIDDLE_Y, MIDDLE_X + hx, MIDDLE_Y + hy, WHITE);
|
||||
}
|
||||
|
||||
void drawHourTick(int hour) {
|
||||
if (hour == 0 || hour == 3 || hour == 6 || hour == 9) return;
|
||||
|
||||
float t = map(hour, 0, 12, 0, 360); // map the 0-60, to "360 degrees"
|
||||
float dx = cos(PI * t / 180.0);
|
||||
float dy = sin(PI * t / 180.0);
|
||||
float radius = (float) CLOCK_RADIUS;
|
||||
int ex = radius * dx + 0.5;
|
||||
int ax = 0.9 * ex;
|
||||
int ey = radius * dy + 0.5;
|
||||
int ay = 0.9 * ey;
|
||||
display.drawLine(MIDDLE_X + ax, MIDDLE_Y + ay, MIDDLE_X + ex, MIDDLE_Y + ey, WHITE);
|
||||
}
|
||||
// Draw an analog clock face
|
||||
void drawFace()
|
||||
{
|
||||
// Draw the clock border
|
||||
display.drawCircle(MIDDLE_X, MIDDLE_Y, CLOCK_RADIUS, WHITE);
|
||||
for (int i=0; i<12; ++i) {
|
||||
// display hour ticks
|
||||
if (i!=0 && i!=3 && i!=6 && i!=9) {
|
||||
int t = map(i*5, 0, 60, 0, 360); // map the 0-60, to "360 degrees"
|
||||
float dx = cos(PI * ((float) t) / 180);
|
||||
float dy = sin(PI * ((float) t) / 180);
|
||||
int ex = CLOCK_RADIUS * dx;
|
||||
int ax = 0.9 * ex;
|
||||
int ey = CLOCK_RADIUS * dy;
|
||||
int ay = 0.9 * ey;
|
||||
display.drawLine(MIDDLE_X + ax, MIDDLE_Y + ay, MIDDLE_X + ex, MIDDLE_Y + ey, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the clock numbers
|
||||
display.setFontType(0); // set font type 0, please see declaration in SFE_MicroOLED.cpp
|
||||
display.setCursor(POS_12_X, POS_12_Y); // points cursor to x=27 y=0
|
||||
display.print(12);
|
||||
display.setCursor(POS_6_X, POS_6_Y);
|
||||
display.print(6);
|
||||
display.setCursor(POS_9_X, POS_9_Y);
|
||||
display.print(9);
|
||||
display.setCursor(POS_3_X, POS_3_Y);
|
||||
display.print(3);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// File generated by LCD Assistant
|
||||
// http://en.radzio.dxp.pl/bitmap_converter/
|
||||
//------------------------------------------------------------------------------
|
||||
//This is the array that holds the Bitmap image. The easiest way to convert a bmp
|
||||
//to an array is to use the LCD Assistant linked above.
|
||||
const uint8_t bender [] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF, 0x5F, 0x5F, 0x5F, 0x5F,
|
||||
0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F,
|
||||
0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F,
|
||||
0x5F, 0xDF, 0xBF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xF9, 0xFE, 0x07, 0x01, 0x00, 0x00, 0xF8, 0xFE, 0xFF,
|
||||
0xFF, 0xFF, 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8,
|
||||
0x00, 0x00, 0x01, 0x07, 0xFE, 0xF9, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF9, 0xE7, 0xDC, 0xB0, 0xA0, 0x40, 0x41, 0x47, 0x4F,
|
||||
0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x4F, 0x47, 0x43, 0x40, 0x40, 0x40, 0x40,
|
||||
0x43, 0x47, 0x4F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x4F, 0x47, 0x43, 0x40,
|
||||
0x40, 0xA0, 0xB0, 0xDE, 0xE7, 0xF9, 0xFE, 0x1F, 0x0F, 0x07, 0x73, 0x79, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
|
||||
0xBF, 0x5F, 0xEF, 0x0F, 0xEF, 0xEF, 0xDF, 0xDF, 0x1F, 0xDF, 0xDF, 0xDF, 0xDF, 0x1F, 0xDF, 0xDF,
|
||||
0xDF, 0xDF, 0xDF, 0x1F, 0xDF, 0xDF, 0xDF, 0xEF, 0x0F, 0xEF, 0xDF, 0xBF, 0x7F, 0xFF, 0xFF, 0xFF,
|
||||
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xBE, 0x9C, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
|
||||
0xB7, 0x6F, 0xEE, 0x00, 0xDE, 0xDE, 0xDE, 0xDD, 0x00, 0xDD, 0xDD, 0xDD, 0xDD, 0x00, 0xDD, 0xDD,
|
||||
0xDD, 0xC5, 0xC1, 0x00, 0xC9, 0xC5, 0xC1, 0x01, 0xC8, 0xC4, 0x42, 0x80, 0xC0, 0xE8, 0xE4, 0xE2,
|
||||
0xE0, 0xE0, 0xEF, 0xEF, 0xE6, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFB, 0xF8, 0xFB, 0xFB, 0xFB, 0xFB, 0xF8, 0xFB, 0xFB,
|
||||
0xFB, 0xFB, 0xFB, 0xF8, 0xFB, 0xFD, 0xFD, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
|
||||
const uint8_t auge_16x16[] = {
|
||||
0B11111110, 0B11111111,
|
||||
0B11111101, 0B11110111,
|
||||
0B11111111, 0B11101111,
|
||||
0B11110111, 0B11111111,
|
||||
0B11100111, 0B11111111,
|
||||
0B11011101, 0B11111110,
|
||||
0B11011110, 0B11110001,
|
||||
0B11110111, 0B11011111,
|
||||
0B11111110, 0B01011111,
|
||||
0B11110111, 0B11101111,
|
||||
0B11111111, 0B11111111,
|
||||
0B11111111, 0B11111111,
|
||||
0B00000000, 0B00000000,
|
||||
0B11111111, 0B11111111,
|
||||
0B00000000, 0B00000000,
|
||||
0B11111111, 0B11111111
|
||||
};
|
||||
|
||||
void Ui::showSplashImage(const uint8_t *image, int width, int height) {
|
||||
display.clearDisplay();
|
||||
displayImage((64-width)/2, (48-height)/2, image, width, height);
|
||||
display.setFontType(0);
|
||||
display.setCursor(0, 48-display.getFontHeight());
|
||||
display.print("FREMOClock");
|
||||
display.display();
|
||||
}
|
||||
|
||||
void Ui::showSplashImage_p(const uint8_t *image, int width, int height) {
|
||||
display.clearDisplay();
|
||||
displayImage_p((64-width)/2, (48-height)/2, image, width, height);
|
||||
display.setFontType(0);
|
||||
display.setCursor(0, 48-display.getFontHeight());
|
||||
display.print("FREMOClock");
|
||||
display.display();
|
||||
}
|
||||
|
||||
void Ui::displayImage(int x, int y, const uint8_t *image, int width, int height) {
|
||||
#if 0
|
||||
for (int row=0; row<height; row++) {
|
||||
for (int col=0; col<width; ++col) {
|
||||
int posx = col + x;
|
||||
int posy = row + y;
|
||||
|
||||
if (posx < 64 && posy < 48) {
|
||||
uint8_t checkByte = image[row * (width/8) + col/8];
|
||||
uint8_t checkBit = col % 8;
|
||||
if (checkByte & _BV(checkBit)) {
|
||||
display.pixel(posx, posy, BLACK, NORM);
|
||||
} else {
|
||||
display.pixel(posx, posy, WHITE, NORM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
display.drawBitmap(x, y, image, width, height, WHITE, BLACK);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ui::displayImage_p(int x, int y, const uint8_t *image, int width, int height) {
|
||||
#if 0
|
||||
for (int row=0; row<height; row++) {
|
||||
for (int col=0; col<width; ++col) {
|
||||
int posx = col + x;
|
||||
int posy = row + y;
|
||||
|
||||
if (posx < 64 && posy < 48) {
|
||||
uint8_t checkByte = pgm_read_byte_near(image + row * (width/8) + col/8); //image[row * (width/8) + col/8];
|
||||
uint8_t checkBit = col % 8;
|
||||
if (checkByte & _BV(checkBit)) {
|
||||
display.pixel(posx, posy, BLACK, NORM);
|
||||
} else {
|
||||
display.pixel(posx, posy, WHITE, NORM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
display.drawBitmap(x, y, image, width, height, BLACK, WHITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ui::displayImageInverted(int x, int y, const uint8_t *image, int width, int height) {
|
||||
#if 0
|
||||
for (int row=0; row<height; row++) {
|
||||
for (int col=0; col<width; ++col) {
|
||||
int posx = col + x;
|
||||
int posy = row + y;
|
||||
|
||||
if (posx < 64 && posy < 48) {
|
||||
uint8_t checkByte = image[row * (width/8) + col/8];
|
||||
uint8_t checkBit = col % 8;
|
||||
if (checkByte & _BV(checkBit)) {
|
||||
display.pixel(posx, posy, WHITE, NORM);
|
||||
} else {
|
||||
display.pixel(posx, posy, BLACK, NORM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
display.drawBitmap(x, y, image, width, height, BLACK, WHITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ui::displayImageInverted_p(int x, int y, const uint8_t *image, int width, int height) {
|
||||
#if 0
|
||||
for (int row=0; row<height; row++) {
|
||||
for (int col=0; col<width; ++col) {
|
||||
int posx = col + x;
|
||||
int posy = row + y;
|
||||
|
||||
if (posx < 64 && posy < 48) {
|
||||
uint8_t checkByte = pgm_read_byte_near(image + row * (width/8) + col/8); // image[row * (width/8) + col/8];
|
||||
uint8_t checkBit = col % 8;
|
||||
if (checkByte & _BV(checkBit)) {
|
||||
display.pixel(posx, posy, WHITE, NORM);
|
||||
} else {
|
||||
display.pixel(posx, posy, BLACK, NORM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
display.drawBitmap(x, y, image, width, height, BLACK, WHITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Ui::begin()
|
||||
{
|
||||
logHeap();
|
||||
display = new Adafruit_SSD1306(4 /* RST GPIO */, Adafruit_SSD1306::RES_64_48);
|
||||
display.begin(SSD1306_SWITCHCAPVCC, 0x3c, true /* reset */);
|
||||
display.display();
|
||||
// set codepage correctly / apply bug fix
|
||||
display.cp437(true);
|
||||
display.setTextWrap(false);
|
||||
display.setTextSize(2);
|
||||
display.setTextColor(WHITE);
|
||||
|
||||
initClockVariables();
|
||||
|
||||
display.clearDisplay();
|
||||
showSplashImage_p(auge_16x16, 16, 16);
|
||||
delay(1000);
|
||||
displayImageInverted_p(0, 0, FREMO_LOGO_bits, FREMO_LOGO_width, FREMO_LOGO_height);
|
||||
|
||||
// drawFace();
|
||||
// drawArms(hours, minutes, seconds);
|
||||
display.display(); // display the memory buffer drawn
|
||||
logHeap();
|
||||
}
|
||||
|
||||
void Ui::setTime(int _hour, int _minute, int _second) {
|
||||
// logHeap();
|
||||
// Debug::out(F("setTime(")); Debug::out(_hour); Debug::out(","); Debug::out(_minute); Debug::out(","); Debug::out(_second); Debug::outln(")");
|
||||
hours = _hour;
|
||||
minutes = _minute;
|
||||
seconds = _second;
|
||||
}
|
||||
|
||||
void Ui::loop()
|
||||
{
|
||||
if (millis() < 2000) return;
|
||||
|
||||
// Check if we need to update seconds, minutes, hours:
|
||||
if (lastDraw + CLOCK_SPEED < millis())
|
||||
{
|
||||
lastDraw = millis();
|
||||
// Add a second, update minutes/hours if necessary:
|
||||
// updateTime();
|
||||
|
||||
// Draw the clock:
|
||||
display.clearDisplay(); // Clear the buffer
|
||||
drawFace(); // Draw the face to the buffer
|
||||
drawArms(hours, minutes, seconds); // Draw arms to the buffer
|
||||
display.display(); // Draw the memory buffer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
src/UI.h
Normal file
30
src/UI.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// FILE: Ui.h
|
||||
// VERSION: 0.1
|
||||
// PURPOSE: User interface / OLED/LCD driver
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef uiControllerLoaded
|
||||
#define uiControllerLoaded true
|
||||
|
||||
#include "appDebug.h"
|
||||
#include "ClockClient.h"
|
||||
|
||||
class Ui {
|
||||
public:
|
||||
Ui() {};
|
||||
static void begin();
|
||||
static void loop();
|
||||
static void showSplashImage(const uint8_t *image, int width, int height);
|
||||
static void showSplashImage_p(const uint8_t *image, int width, int height);
|
||||
static void displayImage(int x, int y, const uint8_t *image, int width, int height);
|
||||
static void displayImage_p(int x, int y, const uint8_t *image, int width, int height);
|
||||
static void displayImageInverted(int x, int y, const uint8_t *image, int width, int height);
|
||||
static void displayImageInverted_p(int x, int y, const uint8_t *image, int width, int height);
|
||||
static void setTime(int _hour, int _minute, int _second);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
8
src/appDebug.h
Normal file
8
src/appDebug.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#if !defined _appDEBUG_H_INCLUDED_
|
||||
#define _appDEBUG_H_INCLUDED_
|
||||
#include "mgos.h"
|
||||
#include "mgos_system.h"
|
||||
|
||||
#define logHeap() LOG(LL_DEBUG,("> Heap: %d - %s: line %d in %s", mgos_get_free_heap_size(), __FILE__,__LINE__,__func__))
|
||||
|
||||
#endif
|
||||
137
src/main.cpp
Normal file
137
src/main.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mgos.h"
|
||||
#include "common/cs_dbg.h"
|
||||
#include "common/json_utils.h"
|
||||
#include "common/platform.h"
|
||||
#include "frozen/frozen.h"
|
||||
#include "mgos_rpc.h"
|
||||
#include "common/cs_file.h"
|
||||
#include "mjs.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "Adafruit_SSD1306dj.h"
|
||||
|
||||
#include "Battery.h"
|
||||
#include "Ui.h"
|
||||
#include "ClockClient.h"
|
||||
#include "appDebug.h"
|
||||
|
||||
static Battery battery = Battery();
|
||||
static ClockClient clockClient = ClockClient();
|
||||
static Ui ui = Ui();
|
||||
|
||||
#if CS_PLATFORM == CS_P_ESP8266
|
||||
/* On ESP-12E there is a blue LED connected to GPIO2 (aka U1TX). */
|
||||
#define LED_GPIO 2
|
||||
#define BUTTON_GPIO 0 /* Usually a "Flash" button. */
|
||||
#define BUTTON_PULL MGOS_GPIO_PULL_UP
|
||||
#define BUTTON_EDGE MGOS_GPIO_INT_EDGE_POS
|
||||
#elif CS_PLATFORM == CS_P_ESP32
|
||||
/* Unfortunately, there is no LED on DevKitC, so this is random GPIO. */
|
||||
#define LED_GPIO 17
|
||||
#define BUTTON_GPIO 0 /* Usually a "Flash" button. */
|
||||
#define BUTTON_PULL MGOS_GPIO_PULL_UP
|
||||
#define BUTTON_EDGE MGOS_GPIO_INT_EDGE_POS
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void blink_timer_cb(void *arg) {
|
||||
bool current_level = mgos_gpio_toggle(LED_GPIO);
|
||||
// LOG(LL_INFO, ("%s", (current_level ? "Tick" : "Tock")));
|
||||
(void) arg;
|
||||
(void) current_level;
|
||||
}
|
||||
|
||||
static void button_cb(int pin, void *arg) {
|
||||
LOG(LL_INFO, ("Click!"));
|
||||
(void) pin;
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
static void getBattery_handler(struct mg_rpc_request_info *ri, void *cb_arg,
|
||||
struct mg_rpc_frame_info *fi, struct mg_str args) {
|
||||
struct mbuf fb;
|
||||
struct json_out out = JSON_OUT_MBUF(&fb);
|
||||
|
||||
mbuf_init(&fb, 20);
|
||||
|
||||
json_printf(&out, "{load: %d, voltage_mV: %d}", batteryGetPercentage(), batteryGetVoltage_mV());
|
||||
|
||||
mgos_gpio_toggle(LED_GPIO);
|
||||
|
||||
mg_rpc_send_responsef(ri, "%.*s", fb.len, fb.buf);
|
||||
ri = NULL;
|
||||
|
||||
mbuf_free(&fb);
|
||||
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
int get_led_gpio_pin(void) {
|
||||
return LED_GPIO;
|
||||
}
|
||||
|
||||
enum mgos_app_init_result mgos_app_init(void) {
|
||||
LOG(LL_DEBUG, ("Starting app %s", mgos_sys_config_get_app_title()));
|
||||
|
||||
// if (!init_fastclock_client(mgos_get_mgr())) return MGOS_APP_INIT_ERROR;
|
||||
|
||||
/* Set up the blinky timer. */
|
||||
mgos_gpio_set_mode(LED_GPIO, MGOS_GPIO_MODE_OUTPUT);
|
||||
mgos_set_timer(1000 /* ms */, true /* repeat */, blink_timer_cb, NULL);
|
||||
|
||||
/* Set up a button handler */
|
||||
mgos_gpio_set_button_handler(BUTTON_GPIO, BUTTON_PULL, BUTTON_EDGE,
|
||||
50 /* debounce_ms */, button_cb, NULL);
|
||||
|
||||
/* Set up RPC */
|
||||
struct mg_rpc *c = mgos_rpc_get_global();
|
||||
mg_rpc_add_handler(c, "ClockClient.GetBattery", "{num: %d}", getBattery_handler, NULL);
|
||||
|
||||
/* Initialize JavaScript engine */
|
||||
int mem1, mem2, mem3;
|
||||
mem1 = mgos_get_free_heap_size();
|
||||
struct mjs *mjs = mjs_create();
|
||||
mem2 = mgos_get_free_heap_size();
|
||||
mjs_set_ffi_resolver(mjs, mgos_dlsym);
|
||||
mjs_err_t err = mjs_exec_file(mjs, "init.js", 1, NULL);
|
||||
if (err != MJS_OK) {
|
||||
mjs_print_error(mjs, stdout, NULL, 1 /* print_stack_trace */);
|
||||
}
|
||||
mem3 = mgos_get_free_heap_size();
|
||||
LOG(LL_INFO, ("mJS memory stat: before init: %d after init: %d after init.js: %d", mem1, mem2, mem3));
|
||||
|
||||
/* Startup app components */
|
||||
ui.begin();
|
||||
clockClient.begin();
|
||||
clockClient.addClockChangeCallback(ui.setTime);
|
||||
|
||||
// not necessary to call the loop, as this is done by the Arduino lib
|
||||
// mgos_timer_callback(1000, MGOS_TIMER_REPEAT, loop, NULL);
|
||||
|
||||
return MGOS_APP_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void checkBattery() {
|
||||
static int lastBatteryReadOn = 0;
|
||||
|
||||
if (millis() - lastBatteryReadOn > 30000) {
|
||||
lastBatteryReadOn = millis();
|
||||
// debug.out("Battery="); debug.out(battery.getPercentage()); debug.outln(" %");
|
||||
LOG(LL_DEBUG, ("Battery=%d%%, %dmV", battery.getPercentage(), battery.getVoltage_mV()));
|
||||
logHeap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void loop(void *args) {
|
||||
checkBattery();
|
||||
clockClient.loop();
|
||||
ui.loop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user