For reference:
core2_clock/README.md at main · codewitch-honey-crisis/core2_clock · GitHub[
^]
Core2 Clock snapshot[
^]
I have some code that I've created for demonstration purposes. It's not meant to be entirely self-explaining, as that would be very verbose and make the actual code hard to navigate.
I intend to write an article about this code.
The problem is I'm way too familiar with my own code to be a fair judge of it. I know some people use it, so it's not entirely incomprehensible, but I guess I just want opinions on the feel of it.
It does a lot with a relatively small amount of code so it's dense in terms of activity, but does that make the cognitive load of it unbearable? I can't tell, because again, it's so familiar to me.
One of the extra wrinkles is I'm demonstrating multiple platform (Arduino and ESP-IDF) and multiple device (M5 Core 2 and M5 Tough) support, so that makes the code a bit forky, but less than one might think if you're familiar with the differences between the platforms I mentioned.
Anyway, I put the code in the What have you tried? section.
I'm not necessarily looking for an exhaustive code review, more just first impressions and things that stand out.
Thanks!
What I have tried:
#if __has_include(<Arduino.h>)
#include <Arduino.h>
#else
#include <freertos/FreeRTOS.h>
#include <stdint.h>
void loop();
static uint32_t millis() {
return pdTICKS_TO_MS(xTaskGetTickCount());
}
#endif
#include <esp_i2c.hpp>
#ifdef M5STACK_CORE2
#include <m5core2_power.hpp>
#endif
#ifdef M5STACK_TOUGH
#include <m5tough_power.hpp>
#endif
#include <bm8563.hpp>
#include <uix.hpp>
#include <gfx.hpp>
#include <wifi_manager.hpp>
#include <ip_loc.hpp>
#include <ntp_time.hpp>
#define OPENSANS_REGULAR_IMPLEMENTATION
#include "assets/OpenSans_Regular.hpp"
#define ICONS_IMPLEMENTATION
#include "assets/icons.hpp"
#include "config.hpp"
#include "ui.hpp"
#include "lcd.hpp"
#ifdef ARDUINO
using namespace arduino;
#else
using namespace esp_idf;
#endif
using namespace gfx;
using namespace uix;
typedef enum {
CS_IDLE,
CS_CONNECTING,
CS_CONNECTED,
CS_FETCHING,
CS_POLLING
} connection_state_t;
#ifdef M5STACK_CORE2
using power_t = m5core2_power;
#endif
#ifdef M5STACK_TOUGH
using power_t = m5tough_power;
#endif
static power_t power(esp_i2c<1,21,22>::instance);
static bm8563 time_rtc(esp_i2c<1,21,22>::instance);
static char time_buffer[64];
static long time_offset = 0;
static ntp_time time_server;
static char time_zone_buffer[64];
static bool time_fetching=false;
static connection_state_t connection_state = CS_IDLE;
static wifi_manager wifi_man;
screen_t main_screen;
svg_clock_t ana_clock(main_screen);
label_t dig_clock(main_screen);
label_t time_zone(main_screen);
canvas_t wifi_icon(main_screen);
canvas_t battery_icon(main_screen);
static void update_time_buffer(time_t time) {
char sz[64];
tm tim = *localtime(&time);
*time_buffer = 0;
strftime(sz, sizeof(sz), "%D ", &tim);
strcat(time_buffer,sz);
strftime(sz, sizeof(sz), "%I:%M %p", &tim);
if(*sz=='0') {
*sz=' ';
}
strcat(time_buffer,sz);
}
static void wifi_icon_paint(surface_t& destination, const srect16& clip, void* state) {
auto px = rgb_pixel<16>(3,7,3);
if(time_fetching) {
px = color_t::white;
}
draw::icon(destination,point16::zero(),faWifi,px);
}
static void battery_icon_paint(surface_t& destination, const srect16& clip, void* state) {
int pct = power.battery_level();
auto px = power.ac_in()?color_t::green:color_t::white;
if(!power.ac_in() && pct<25) {
px=color_t::red;
}
draw::icon(destination,point16::zero(),faBatteryEmpty,px);
if(pct==100) {
draw::filled_rectangle(destination,rect16(3,7,22,16),px);
} else {
draw::filled_rectangle(destination,rect16(4,9,4+(0.18f*pct),14),px);
}
}
#ifdef ARDUINO
void setup() {
Serial.begin(115200);
#else
extern "C" void app_main() {
#endif
power.initialize(); panel_init(); power.lcd_voltage(3.0);
time_rtc.initialize();
puts("Clock booted");
if(power.charging()) {
puts("Charging");
} else {
puts("Not charging"); }
main_screen.dimensions({320,240});
main_screen.buffer_size(panel_transfer_buffer_size);
main_screen.buffer1(panel_transfer_buffer1);
main_screen.buffer2(panel_transfer_buffer2);
main_screen.background_color(color_t::black);
ana_clock.bounds(srect16(0,0,127,127).center_horizontal(main_screen.bounds()));
ana_clock.face_color(color32_t::light_gray);
auto px = ana_clock.second_color();
px.template channel<channel_name::A>(
decltype(px)::channel_by_name<channel_name::A>::max/2);
ana_clock.second_color(px);
px = ana_clock.minute_color();
px.template channelr<channel_name::A>(0.5f);
ana_clock.minute_color(px);
ana_clock.hour_border_color(color32_t::gray);
ana_clock.minute_border_color(ana_clock.hour_border_color());
ana_clock.face_color(color32_t::black);
ana_clock.face_border_color(color32_t::black);
main_screen.register_control(ana_clock);
dig_clock.bounds(
srect16(0,0,main_screen.bounds().x2,39)
.offset(0,128));
update_time_buffer(time_rtc.now()); dig_clock.text(time_buffer);
dig_clock.text_open_font(&text_font);
dig_clock.text_line_height(35);
dig_clock.text_color(color32_t::white);
dig_clock.text_justify(uix_justify::top_middle);
main_screen.register_control(dig_clock);
const uint16_t tz_top = dig_clock.bounds().y1+dig_clock.dimensions().height;
time_zone.bounds(srect16(0,tz_top,main_screen.bounds().x2,tz_top+40));
time_zone.text_open_font(&text_font);
time_zone.text_line_height(30);
time_zone.text_color(color32_t::light_sky_blue);
time_zone.text_justify(uix_justify::top_middle);
main_screen.register_control(time_zone);
wifi_icon.bounds(
srect16(spoint16(0,0),(ssize16)wifi_icon.dimensions())
.offset(main_screen.dimensions().width-
wifi_icon.dimensions().width,0));
wifi_icon.on_paint_callback(wifi_icon_paint);
main_screen.register_control(wifi_icon);
battery_icon.bounds(
(srect16)faBatteryEmpty.dimensions().bounds());
battery_icon.on_paint_callback(battery_icon_paint);
wifi_icon.on_touch_callback([](size_t locations_size, const spoint16* locations, void* state){
if(connection_state==CS_IDLE) {
connection_state = CS_CONNECTING;
}
},nullptr);
main_screen.register_control(battery_icon);
rgba_pixel<32> transparent(0,0,0,0);
panel_set_active_screen(main_screen);
#ifndef ARDUINO
while(1) {
loop();
vTaskDelay(5);
}
#endif
}
void loop()
{
static uint32_t connection_refresh_ts = 0;
static uint32_t time_ts = 0;
switch(connection_state) {
case CS_IDLE:
if(connection_refresh_ts==0 || millis() > (connection_refresh_ts+(time_refresh_interval*1000))) {
connection_refresh_ts = millis();
connection_state = CS_CONNECTING;
}
break;
case CS_CONNECTING:
time_ts = 0;
time_fetching = true;
wifi_icon.invalidate();
if(wifi_man.state()!=wifi_manager_state::connected && wifi_man.state()!=wifi_manager_state::connecting) {
puts("Connecting to network...");
wifi_man.connect(wifi_ssid,wifi_pass);
connection_state =CS_CONNECTED;
} else if(wifi_man.state()==wifi_manager_state::connected) {
connection_state = CS_CONNECTED;
}
break;
case CS_CONNECTED:
if(wifi_man.state()==wifi_manager_state::connected) {
puts("Connected.");
connection_state = CS_FETCHING;
} else if(wifi_man.state()==wifi_manager_state::error) {
connection_refresh_ts = 0; connection_state = CS_IDLE;
time_fetching = false;
}
break;
case CS_FETCHING:
puts("Retrieving time info...");
connection_refresh_ts = millis();
ip_loc::fetch(nullptr,nullptr,&time_offset,nullptr,0,nullptr,0,time_zone_buffer,sizeof(time_zone_buffer));
connection_state = CS_POLLING;
time_ts = millis(); time_server.begin_request();
break;
case CS_POLLING:
if(time_server.request_received()) {
const int latency_offset = (millis()-time_ts)/1000;
time_rtc.set((time_t)(time_server.request_result()+time_offset+latency_offset));
puts("Clock set.");
update_time_buffer(time_rtc.now());
dig_clock.invalidate();
time_zone.text(time_zone_buffer);
connection_state = CS_IDLE;
puts("Turning WiFi off.");
wifi_man.disconnect(true);
time_fetching = false;
wifi_icon.invalidate();
} else if(millis()>time_ts+(wifi_fetch_timeout*1000)) {
puts("Retrieval timed out. Retrying.");
connection_state = CS_FETCHING;
}
break;
}
time_t time = time_rtc.now();
ana_clock.time(time);
if(0==(time%60)) {
update_time_buffer(time);
dig_clock.invalidate();
}
static int bat_level = power.battery_level();
if((int)power.battery_level()!=bat_level) {
bat_level = power.battery_level();
battery_icon.invalidate();
}
static bool ac_in = power.ac_in();
if(power.ac_in()!=ac_in) {
ac_in = power.ac_in();
battery_icon.invalidate();
}
time_server.update();
panel_update();
}