#include #include #include #include "config.h" //#include "shalom.h" #include "shalombg.h" //#include "salam.h" #include "salambg.h" #include "togetherbg.h" #include "talis.h" #include "metta.h" #include "loving.h" #include "kindness.h" #include "mybird.h" #include "yourbird.h" #define DEBUG false // note: it's easier to read the MAC address if DEBUG is false #define BUTTON1PIN ((gpio_num_t)35) #define BUTTON2PIN ((gpio_num_t)0) #define BRIGHTNESS 63 // 0..255 #define INTRO_FREEZE_MILLIS 4000 #define INTRO_SCROLL_MILLIS 2000 #define PING_START_MILLIS 8000 // should be > INTRO_FREEZE_MILLIS + INTRO_SCROLL_MILLIS #define METTA_FREEZE_MILLIS 1000 #define METTA_SCROLL_MILLIS 2000 #define OUTRO_SCROLL_MILLIS 2000 #define OUTRO_FREEZE_MILLIS 1000 unsigned long outro_start; #define MYBIRD_WIDTH 30 #define MYBIRD_HEIGHT 36 #define MYBIRD_X 20 #define MYBIRD_Y 10 #define YOURBIRD_WIDTH 32 #define YOURBIRD_HEIGHT 33 #define YOURBIRD_X 53 #define YOURBIRD_Y 5 #define STATUS_SEND_MILLIS 1000 #define STATUS_RECEIVE_MILLIS 1058 // 2X23X23 to make things resonate #define STATUS_COLOR TFT_SKYBLUE #define SEND_INTERVAL 3000 unsigned long next_send = 0; String my_mac; bool is_salam; bool metta_from_you; unsigned long metta_from_me_since = 0; #define MANTRA_I "May I be filled with loving kindness" #define MANTRA_YOU "May you be filled with loving kindness" uint8_t peer_mac_addr[6]; esp_now_peer_info_t peerInfo; bool nearby = false; #define NUM_FRAMES 9 enum frameType { FRAME_ME, FRAME_US, FRAME_U }; frameType frames[NUM_FRAMES] = { FRAME_ME, FRAME_ME, FRAME_US, FRAME_U, FRAME_U, FRAME_US, FRAME_US, FRAME_US, FRAME_US }; const unsigned short *frame2image(frameType frame, bool is_salam, bool metta_from_me, bool metta_from_you) { if (!metta_from_me || !metta_from_you) { return is_salam ? salambg : shalombg; } switch (frame) { case FRAME_ME: return is_salam ? salambg : shalombg; case FRAME_US: return togetherbg; case FRAME_U: return is_salam ? shalombg : salambg; } } int current_frame = 0; unsigned long last_flip = 0; #define DURATION 200 TFT_eSPI tft = TFT_eSPI(); TFT_eSprite background = TFT_eSprite(&tft); TFT_eSprite leftSprite = TFT_eSprite(&tft); TFT_eSprite rightSprite = TFT_eSprite(&tft); TFT_eSprite mybirdSprite = TFT_eSprite(&tft); TFT_eSprite yourbirdSprite = TFT_eSprite(&tft); TFT_eSprite statusSprite = TFT_eSprite(&tft); // lifted from MacAddress.c bool str2mac(char *buf, uint8_t *mac) { char cs[18]; char *token; char *next; //Unused but required int i; strncpy(cs, buf, sizeof(cs)); //strtok modifies the buffer: copy to working buffer. for (i = 0; i < 6; i++) { token = strtok((i == 0) ? cs : NULL, ":"); //Find first or next token if (!token) { //No more tokens found return false; } mac[i] = strtol(token, &next, 16); } return true; } void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { nearby = (status == ESP_NOW_SEND_SUCCESS); if (!nearby) { metta_from_you = false; } if (DEBUG) { Serial.println(nearby ? "||" : "|"); } } void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) { metta_from_you = (data_len == strlen(MANTRA_YOU)) && !strncmp((const char *)data, MANTRA_YOU, data_len); if (DEBUG) { // char macStr[18]; // snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", // mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); // Serial.print("Packet Recv from: "); Serial.println(macStr); char msg[64]; int msglen = min(data_len, 63); strncpy(msg, (const char *)data, msglen); msg[msglen] = '\0'; Serial.print("<<< "); Serial.println(msg); Serial.print(metta_from_me_since ? "_/" : "__"); Serial.println(metta_from_you ? "\\_" : "__"); } } void setup() { pinMode(BUTTON1PIN, INPUT_PULLUP); pinMode(BUTTON2PIN, INPUT_PULLUP); Serial.begin(115200); delay(1000); Serial.println("=== Talis-Metta ==="); // determine peer mac address my_mac = WiFi.macAddress(); Serial.print("My MAC: "); Serial.println(my_mac); if (my_mac.equals(SHALOM)) { if (DEBUG) { Serial.println("Shalom"); } is_salam = false; str2mac(SALAM, peer_mac_addr); } else { if (DEBUG) { Serial.println("Salam"); } is_salam = true; str2mac(SHALOM, peer_mac_addr); } outro_start = 0; metta_from_me_since = 0; metta_from_you = false; WiFi.mode(WIFI_STA); esp_now_init(); memcpy(peerInfo.peer_addr, peer_mac_addr, 6); peerInfo.channel = 0; peerInfo.encrypt = false; esp_now_add_peer(&peerInfo); esp_now_register_send_cb(OnDataSent); esp_now_register_recv_cb(OnDataRecv); tft.init(); tft.setRotation(3); // was 1 tft.setSwapBytes(true); analogWrite(TFT_BL,BRIGHTNESS); tft.fillScreen(TFT_BLACK); background.createSprite(240, 135); background.setSwapBytes(true); leftSprite.createSprite(120, 135); leftSprite.setSwapBytes(true); rightSprite.createSprite(120, 135); rightSprite.setSwapBytes(true); mybirdSprite.createSprite(MYBIRD_WIDTH, MYBIRD_HEIGHT); mybirdSprite.setSwapBytes(true); mybirdSprite.pushImage(0, 0, MYBIRD_WIDTH, MYBIRD_HEIGHT, mybird); yourbirdSprite.createSprite(YOURBIRD_WIDTH, YOURBIRD_HEIGHT); yourbirdSprite.setSwapBytes(true); yourbirdSprite.pushImage(0, 0, YOURBIRD_WIDTH, YOURBIRD_HEIGHT, yourbird); statusSprite.createSprite(240, 18); statusSprite.setSwapBytes(true); } void do_scroll(const unsigned short *left_image, const unsigned short *right_image, unsigned long start_time, unsigned long freeze_duration, unsigned long scroll_duration, bool is_reverse) { unsigned long nowmillis = millis(); if (nowmillis >= start_time && nowmillis < start_time + freeze_duration + scroll_duration) { unsigned long freeze_start = is_reverse ? start_time + scroll_duration : start_time; unsigned long scroll_start = is_reverse ? start_time : start_time + freeze_duration; leftSprite.pushImage(0, 0, 120, 135, left_image); rightSprite.pushImage(0, 0, 120, 135, right_image); if (nowmillis >= freeze_start && nowmillis < freeze_start + freeze_duration) { leftSprite.pushToSprite(&background, 0, 0); rightSprite.pushToSprite(&background, 120, 0); } else { long scroll_pixels = 123 * (nowmillis - scroll_start) / scroll_duration; if (is_reverse) { scroll_pixels = 123 - scroll_pixels; } leftSprite.pushToSprite(&background, -scroll_pixels, 0); rightSprite.pushToSprite(&background, 120 + scroll_pixels, 0); } } } void do_status(bool is_send) { unsigned long cycle = is_send ? STATUS_SEND_MILLIS : STATUS_RECEIVE_MILLIS; float quotient = float(millis() % cycle) / cycle; if (!is_send) { quotient = 1.0 - quotient; } int32_t r_in = 130 * quotient; // a bit larger than 240/2 int32_t r_out = 130 * pow(quotient, 0.75); // grows faster statusSprite.fillSprite(TFT_BLACK); statusSprite.fillEllipse(120, 9, r_out, 10, STATUS_COLOR); statusSprite.fillEllipse(120, 9, r_in, 10, TFT_BLACK); statusSprite.pushToSprite(&background, 0, 116, TFT_BLACK); } void loop() { unsigned long nowmillis = millis(); if (digitalRead(BUTTON1PIN) == LOW) { outro_start = nowmillis; } if (digitalRead(BUTTON2PIN) == LOW) { metta_from_me_since = nowmillis; } if (nowmillis > next_send) { if (DEBUG) { Serial.print(">>> "); Serial.println(metta_from_me_since ? MANTRA_YOU : MANTRA_I); } esp_now_send( peerInfo.peer_addr, metta_from_me_since ? (const uint8_t *)MANTRA_YOU : (const uint8_t *)MANTRA_I, metta_from_me_since ? strlen(MANTRA_YOU) : strlen(MANTRA_I)); next_send = nowmillis + SEND_INTERVAL; } if (nowmillis > PING_START_MILLIS) { if ((nowmillis - last_flip) > DURATION || nowmillis < last_flip) { current_frame = (current_frame + 1) % NUM_FRAMES; last_flip = nowmillis; background.pushSprite(0, 0); } } else { current_frame = 0; } // FRAME_ME or animation frame background.pushImage( 0, 0, 240, 135, frame2image(frames[current_frame], is_salam, metta_from_me_since, metta_from_you)); // mybird (always on) if (true /* !metta_from_me_since || frames[current_frame] == FRAME_ME || frames[current_frame] == FRAME_US */) { mybirdSprite.pushToSprite(&background, MYBIRD_X, MYBIRD_Y, TFT_BLACK); } // yourbird (if nearby) if (nearby /* && (!metta_from_you || frames[current_frame] == FRAME_U || frames[current_frame] == FRAME_US) */) { yourbirdSprite.pushToSprite(&background, YOURBIRD_X, YOURBIRD_Y, TFT_BLACK); } // "send" status if (metta_from_me_since) { do_status(true); } // "receive" status if (metta_from_you) { do_status(false); } // Scroll overlays if (outro_start) { // Outro scroll overlay if (nowmillis - outro_start > OUTRO_FREEZE_MILLIS + OUTRO_SCROLL_MILLIS) { // outro over. shut down esp_sleep_enable_ext0_wakeup(BUTTON1PIN, LOW); esp_deep_sleep_start(); } else { // do outro reverse scroll and freeze do_scroll(talis, metta, outro_start, OUTRO_FREEZE_MILLIS, OUTRO_SCROLL_MILLIS, true); } } else if (nowmillis < INTRO_FREEZE_MILLIS + INTRO_SCROLL_MILLIS) { // Intro scroll overlay do_scroll(talis, metta, 0, INTRO_FREEZE_MILLIS, INTRO_SCROLL_MILLIS, false); } else if (metta_from_me_since && nowmillis < metta_from_me_since + METTA_FREEZE_MILLIS + METTA_SCROLL_MILLIS) { do_scroll(loving, kindness, metta_from_me_since, METTA_FREEZE_MILLIS, METTA_SCROLL_MILLIS, false); } background.pushSprite(0, 0); }