MVP: Basic working clock and alarm
This commit is contained in:
commit
b6ac19a54d
18 changed files with 22909 additions and 0 deletions
423
main/main.c
Executable file
423
main/main.c
Executable file
|
|
@ -0,0 +1,423 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_netif.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_netif_sntp.h"
|
||||
|
||||
#include "nvs.h"
|
||||
|
||||
#include "epd7in5.h"
|
||||
#include "wifi_manager.h"
|
||||
|
||||
#include "OverpassMono_Bitmaps.h"
|
||||
|
||||
|
||||
#include "esp_tls.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/i2s_std.h"
|
||||
|
||||
static const char TAG[] = "main";
|
||||
|
||||
enum {
|
||||
WIFI_CONNECTED,
|
||||
WIFI_LOST,
|
||||
ACCESS_POINT,
|
||||
} WifiState;
|
||||
|
||||
struct {
|
||||
bool radio;
|
||||
} GlobalState;
|
||||
|
||||
typedef struct {
|
||||
uint8_t event_id;
|
||||
uint32_t time;
|
||||
} TimerEvent;
|
||||
|
||||
typedef struct {
|
||||
uint8_t event_id;
|
||||
} AlarmEvent;
|
||||
|
||||
QueueHandle_t wifiQueue;
|
||||
QueueHandle_t timerEinkQueue;
|
||||
QueueHandle_t timerAlarmQueue;
|
||||
QueueHandle_t alarmQueue;
|
||||
|
||||
void draw_char(uint8_t *image, int x, int y, char c) {
|
||||
uint32_t char_offset = (c - '0') * 16 * 176;
|
||||
for(int i=0; i<176; ++i) {
|
||||
memcpy(&image[x + (y+i)*EPD_WIDTH/8], &OverpassMono_Bitmaps[i*16+char_offset], 16);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_text(uint8_t *image, int x, int y, const char *text, int length)
|
||||
{
|
||||
//89x130
|
||||
for(int i=0; i<length; ++i) {
|
||||
if(text[i] < '0' || text[i] > '9') {
|
||||
ESP_LOGE(TAG, "Invalid character '%c' in text: %s", text[i], text);
|
||||
return;
|
||||
}
|
||||
draw_char(image, x + i * 17, y, text[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void eink_task(void *pvParameter)
|
||||
{
|
||||
static uint8_t image[EPD_WIDTH * EPD_HEIGHT / 8];
|
||||
memset(image, 0x00, sizeof(image));
|
||||
|
||||
epd7in5_v3_t epd;
|
||||
epd7in5_v3_create(&epd, SPI2_HOST);
|
||||
|
||||
while(true) {
|
||||
|
||||
// Wait for time update from timerQueue
|
||||
TimerEvent event;
|
||||
if (xQueueReceive(timerEinkQueue, &event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to receive timer event from queue");
|
||||
continue;
|
||||
}
|
||||
|
||||
char buffer[5] = {0};
|
||||
memcpy(buffer, &event.time, sizeof(event.time));
|
||||
|
||||
memset(image, 0x00, sizeof(image));
|
||||
|
||||
epd7in5_v3_init(&epd);
|
||||
draw_text(image, 0, 0, buffer, 4);
|
||||
epd7in5_v3_busy_wait(&epd);
|
||||
epd7in5_v3_display(&epd, image);
|
||||
epd7in5_v3_busy_wait(&epd);
|
||||
epd7in5_v3_sleep(&epd);
|
||||
|
||||
vTaskDelay( pdMS_TO_TICKS(500) );
|
||||
}
|
||||
}
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
#define SAMPLE_RATE 44100
|
||||
#define TONE_HZ 853
|
||||
#define TONE_2_HZ 960
|
||||
#define I2S_NUM 0
|
||||
#define PI 3.14159265
|
||||
#define BITS_PER_SAMPLE 16
|
||||
#define CHANNELS 2
|
||||
#define BUFFER_SAMPLES 2048*4
|
||||
|
||||
static i2s_chan_handle_t tx_handle;
|
||||
|
||||
void radio_task(void *pvParameter)
|
||||
{
|
||||
bool radioActive = false;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
|
||||
// --- Clock configuration ---
|
||||
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE);
|
||||
|
||||
// --- Slot configuration ---
|
||||
i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(
|
||||
BITS_PER_SAMPLE,
|
||||
I2S_SLOT_MODE_MONO
|
||||
);
|
||||
slot_cfg.slot_mask = I2S_STD_SLOT_LEFT | I2S_STD_SLOT_RIGHT;
|
||||
|
||||
// --- GPIO configuration ---
|
||||
i2s_std_gpio_config_t gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = GPIO_NUM_35,
|
||||
.ws = GPIO_NUM_36,
|
||||
.dout = GPIO_NUM_37,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false
|
||||
}
|
||||
};
|
||||
|
||||
// --- Initialize the TX channel in STD mode ---
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = clk_cfg,
|
||||
.slot_cfg = slot_cfg,
|
||||
.gpio_cfg = gpio_cfg
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
|
||||
|
||||
// --- Generate and send sine wave ---
|
||||
int16_t *samples = malloc(BUFFER_SAMPLES * sizeof(int16_t));
|
||||
if (!samples) {
|
||||
ESP_LOGE("I2S", "Failed to allocate memory for samples");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
size_t level_up_size = sizeof(level_up) / sizeof(level_up[0]);
|
||||
|
||||
AlarmEvent event;
|
||||
while(true) {
|
||||
if(!radioActive) {
|
||||
if(xQueueReceive(alarmQueue, &event, portMAX_DELAY) && event.event_id == 1) {
|
||||
ESP_LOGI(TAG, "Alarm activated, starting radio");
|
||||
radioActive = true;
|
||||
}
|
||||
} else {
|
||||
if(xQueueReceive(alarmQueue, &event, 0) && event.event_id == 0) {
|
||||
ESP_LOGI(TAG, "Alarm deactivated, stopping radio");
|
||||
radioActive = false;
|
||||
}
|
||||
|
||||
//Do radio shit
|
||||
for (int i = 0; i < BUFFER_SAMPLES; ++i) {
|
||||
int16_t sample = level_up[(i+offset) % level_up_size];
|
||||
samples[i] = sample / 4;
|
||||
}
|
||||
|
||||
size_t bytes_to_write = BUFFER_SAMPLES * sizeof(int16_t);
|
||||
size_t bytes_written = 0;
|
||||
ESP_ERROR_CHECK(i2s_channel_write(tx_handle, samples, bytes_to_write, &bytes_written, portMAX_DELAY));
|
||||
offset += bytes_written/2;
|
||||
|
||||
vTaskDelay(1);
|
||||
|
||||
}
|
||||
}
|
||||
free(samples);
|
||||
}
|
||||
|
||||
void alarm_task(void *pvParameter)
|
||||
{
|
||||
bool alarmActive = false;
|
||||
|
||||
int alarmStart = 830; // 20:55 in HHMM format
|
||||
int alarmEnd = 900; // 20:56 in HHMM format
|
||||
|
||||
TimerEvent event;
|
||||
while(true) {
|
||||
if (xQueueReceive(timerAlarmQueue, &event, portMAX_DELAY) == pdTRUE) {
|
||||
ESP_LOGI(TAG, "Time event received");
|
||||
int time = atoi((const char *)&event.time);
|
||||
if (time >= alarmStart && time < alarmEnd && !alarmActive) {
|
||||
alarmActive = true;
|
||||
AlarmEvent alarm_event;
|
||||
alarm_event.event_id = 1; // or any other event ID you want to use
|
||||
if (xQueueSend(alarmQueue, &alarm_event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to send alarm event to queue");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Alarm event pushed to queue");
|
||||
}
|
||||
} else if (time >= alarmEnd && alarmActive) {
|
||||
alarmActive = false;
|
||||
AlarmEvent alarm_event;
|
||||
alarm_event.event_id = 0; // or any other event ID you want to use
|
||||
if (xQueueSend(alarmQueue, &alarm_event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to send alarm event to queue");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Alarm event pushed to queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool timeSet = false;
|
||||
|
||||
void time_update_task(void *pvParameter)
|
||||
{
|
||||
TimerEvent event;
|
||||
char lastMinute = '-';
|
||||
|
||||
while(!timeSet) {
|
||||
// Wait for time to be set by SNTP
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
while(true) {
|
||||
// check if the last minute has changed and push to queue if so
|
||||
time_t now;
|
||||
struct tm timeinfo = { 0 };
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
char buffer[5];
|
||||
strftime(buffer, sizeof(buffer), "%H%M", &timeinfo);
|
||||
if (buffer[3] != lastMinute) {
|
||||
lastMinute = buffer[3];
|
||||
event.event_id = WIFI_CONNECTED; // or any other event ID you want to use
|
||||
memcpy(&event.time, buffer, sizeof(event.time));
|
||||
if (xQueueSend(timerEinkQueue, &event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to send timer event to queue");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Pushed time update to queue: %s", buffer);
|
||||
}
|
||||
if (xQueueSend(timerAlarmQueue, &event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to send timer event to queue");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Pushed time update to queue: %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500)); //Check every half second
|
||||
}
|
||||
}
|
||||
|
||||
void time_sync_notification_cb(struct timeval *tv)
|
||||
{
|
||||
timeSet = true;
|
||||
ESP_LOGI(TAG, "Notification of a time synchronization event");
|
||||
}
|
||||
|
||||
//RTC_DATA_ATTR static int boot_count = 0;
|
||||
static void obtain_time(void* pvParameter)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SNTP");
|
||||
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
|
||||
config.start = false;
|
||||
config.server_from_dhcp = false;
|
||||
config.renew_servers_after_new_IP = false;
|
||||
config.index_of_first_server = 1;
|
||||
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP;
|
||||
config.sync_cb = time_sync_notification_cb;
|
||||
esp_netif_sntp_init(&config);
|
||||
|
||||
setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1);
|
||||
tzset();
|
||||
|
||||
while(true) {
|
||||
ESP_LOGI(TAG, "Starting SNTP");
|
||||
esp_netif_sntp_start();
|
||||
time_t now = 0;
|
||||
struct tm timeinfo = { 0 };
|
||||
int retry = 0;
|
||||
const int retry_count = 15;
|
||||
while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) == ESP_ERR_TIMEOUT && ++retry < retry_count) {
|
||||
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
|
||||
}
|
||||
time(&now);
|
||||
localtime_r(&now, &timeinfo);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(60 * 60 * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
void cb_connection_ok(void *pvParameter){
|
||||
ip_event_got_ip_t* param = (ip_event_got_ip_t*)pvParameter;
|
||||
|
||||
char str_ip[16];
|
||||
esp_ip4addr_ntoa(¶m->ip_info.ip, str_ip, IP4ADDR_STRLEN_MAX);
|
||||
ESP_LOGI(TAG, "I have a connection and my IP is %s!", str_ip);
|
||||
|
||||
// Notify the radio task that we have a connection
|
||||
int wifi_event = WIFI_CONNECTED;
|
||||
if (xQueueSend(wifiQueue, &wifi_event, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Failed to send WIFI_CONNECTED event to queue");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Sent WIFI_CONNECTED event to queue");
|
||||
}
|
||||
}
|
||||
|
||||
//void audio_task(void* pvParameter);
|
||||
//void http_stream_task(void *pvParameter);
|
||||
//void https_insecure_task(void *pv);
|
||||
|
||||
void app_main()
|
||||
{
|
||||
esp_log_level_set("esp-tls-mbedtls", ESP_LOG_DEBUG);
|
||||
esp_log_level_set("TLSv1", ESP_LOG_DEBUG);
|
||||
//mbedtls_debug_set_threshold(4);
|
||||
|
||||
wifiQueue = xQueueCreate(2, sizeof(int));
|
||||
timerEinkQueue = xQueueCreate(2, sizeof(TimerEvent));
|
||||
timerAlarmQueue = xQueueCreate(2, sizeof(TimerEvent));
|
||||
alarmQueue = xQueueCreate(2, sizeof(AlarmEvent));
|
||||
|
||||
wifi_manager_start();
|
||||
wifi_manager_set_callback(WM_EVENT_STA_GOT_IP, &cb_connection_ok);
|
||||
xTaskCreatePinnedToCore(&radio_task, "radio", 3072, NULL, 1, NULL, 1);
|
||||
xTaskCreatePinnedToCore(&eink_task, "eink", 3072, NULL, 1, NULL, 1);
|
||||
|
||||
xTaskCreate(&obtain_time, "sntp", 3072, NULL, 10, NULL);
|
||||
xTaskCreate(&time_update_task, "timer", 3072, NULL, 10, NULL);
|
||||
//xTaskCreate(&audio_task, "timer", 3072, NULL, 10, NULL);
|
||||
xTaskCreate(&alarm_task, "timer", 3072, NULL, 10, NULL);
|
||||
//xTaskCreate(https_insecure_task, "https_insec", 8192, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// HTTP test code
|
||||
//
|
||||
//void https_get_insecure(const char *host, const char *path)
|
||||
//{
|
||||
// const int port = 443;
|
||||
//
|
||||
// // 1) Allocate the TLS handle
|
||||
// esp_tls_t *tls = esp_tls_init();
|
||||
// if (!tls) {
|
||||
// ESP_LOGE(TAG, "Failed to init TLS handle");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Insecure TLS config: no CA, skip CN
|
||||
// esp_tls_cfg_t tls_cfg = {
|
||||
// .cacert_buf = NULL,
|
||||
// .cacert_bytes = 0,
|
||||
// .skip_common_name = true,
|
||||
// // Force TLS 1.2 only:
|
||||
// .tls_version = ESP_TLS_VER_TLS_1_2,
|
||||
// // Advertise HTTP/1.1 via ALPN (many servers require this for HTTPS):
|
||||
// .alpn_protos = (const char*[]){"http/1.1", NULL},
|
||||
// };
|
||||
//
|
||||
// // This will send the SNI extension = host
|
||||
// int ret = esp_tls_conn_new_sync(host, strlen(host), port, &tls_cfg, tls);
|
||||
// if (ret != 1) {
|
||||
// ESP_LOGE(TAG, "TLS handshake failed, esp_tls_conn_new_sync returned %d", ret);
|
||||
// esp_tls_conn_destroy(tls);
|
||||
// return;
|
||||
// }
|
||||
// ESP_LOGI(TAG, "TLS handshake succeeded (insecure)");
|
||||
//
|
||||
// // Send a minimal GET
|
||||
// char req[256];
|
||||
// int len = snprintf(req, sizeof(req),
|
||||
// "GET %s HTTP/1.1\r\n"
|
||||
// "Host: %s\r\n"
|
||||
// "Connection: close\r\n\r\n",
|
||||
// path, host);
|
||||
// esp_tls_conn_write(tls, (const unsigned char *)req, len);
|
||||
// // Read and print
|
||||
// char buf[128];
|
||||
// int r;
|
||||
// do {
|
||||
// r = esp_tls_conn_read(tls, (unsigned char*)buf, sizeof(buf)-1);
|
||||
// if (r > 0) {
|
||||
// buf[r] = '\0';
|
||||
// printf("%s", buf);
|
||||
// }
|
||||
// } while (r > 0 || r == ESP_TLS_ERR_SSL_WANT_READ);
|
||||
// esp_tls_conn_destroy(tls);
|
||||
//}
|
||||
//
|
||||
//void https_insecure_task(void *pv)
|
||||
//{
|
||||
// // Wait for WiFi to be connected (from wifiqueue)
|
||||
// int event;
|
||||
// if( xQueueReceive(wifiQueue, &event, portMAX_DELAY) != pdTRUE ) {
|
||||
// ESP_LOGE(TAG, "Failed to receive WIFI_CONNECTED event from queue");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// https_get_insecure("listen.moe", "/stream");
|
||||
//}
|
||||
Loading…
Add table
Add a link
Reference in a new issue