* Project Real-time Bus Arrival Alarm * Parts - WIZwiki-W7500 Platform - Seeed Grove OLED display - Seeed Grove 4-digit display - Seeed Grove Buzzer * Detailed description Real Time Bus Information will show you when your bus is due to arrive at your bus stop. * Target Bus Information System - GyeongGi Bus Information, Korea (www.gbis.go.kr)

Dependencies:   WIZnetInterface mbed DigitDisplay HTTPClient NTPClient SeeedGrayOLED

Revision:
0:879add9a219d
diff -r 000000000000 -r 879add9a219d main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Aug 26 05:12:36 2015 +0000
@@ -0,0 +1,874 @@
+#include "mbed.h"
+#include "EthernetInterface.h"
+
+#include "HTTPClient.h"     // HTTP Client library
+#include "NTPClient.h"      // NTP Client library (Network Time Protocol)
+
+#include "DigitDisplay.h"   // Seeed Grove: 4-digit Display
+#include "SeeedGrayOLED.h"  // Seeed Grove: OLED Display 0.96" 96x96
+
+Serial pc(USBTX, USBRX);
+
+PwmOut Buzzer(D6);
+DigitDisplay display(D0, D1); // 4-Digit Display connected to UART Grove connector
+SeeedGrayOLED SeeedGrayOled(D14, D15);
+DigitalOut myled(LED1);
+
+Ticker tick;
+
+////////////////////////////////////////////////////////////////
+// Defines
+////////////////////////////////////////////////////////////////
+
+/* User adjustable defines */
+////////////////////////////////////////////////////////////////
+/* Debug Message Enable */
+#define _DEBUG_BUS_ARRIVAL_ALARM_       0
+
+/* Number of Displayed Bus Info */
+#define MAX_BUS_INFO                    3       // The maximum bus information request can be up to Three (3)
+
+/* Display mode */
+#define DISPLAY_MODE_CHANGE_CNT_SEC     10      // The OLED display holding time(sec) for each bus information displayed
+#define BUS_NUM_BLINK_ENABLE            1       // blink the 4-digit display when displayed bus numbers
+
+/* Alarm Sound Enabled and Time */
+#define ALARM_SOUND_ENABLE              1
+#define SOUND_START_HOUR                6
+#define SOUND_START_MIN                 15
+#define SOUND_END_HOUR                  07
+#define SOUND_END_MIN                   00
+
+/* HTTP client */
+//#define MAX_HTTPC_RETRY_CNT             2       // not used
+////////////////////////////////////////////////////////////////
+
+/* Buzzer */
+#define VOLUME                          0.2     // 0.02
+#define BPM                             100.0
+
+/* Display mode define */
+#define DISPLAY_TIME_MODE               0   // do not change!
+
+/* Bus and Station (Busstop) info for GBIS openAPI */
+#define DEFAULT_STATION_NUM             38270 //(Gwangju -> Seoul)
+#define DEFAULT_STATION_ID              234000302       
+// for test
+//#define DEFAULT_STATION_NUM           38269 //(Seoul -> Gwangju)
+//#define DEFAULT_STATION_ID            234000294       
+
+typedef struct
+{    
+    uint8_t * busNum;       // Bus number, This value should be input at initialize stage
+    uint16_t stationNum;    // Station number, notused, This value should be input at initialize stage
+    uint32_t routeId;       // Bus route id for openAPI querying (openapi.gbis.go.kr)   // This value should be input at initialize stage
+    uint32_t stationId;     // Bus station id for openAPI querying (openapi.gbis.go.kr) // This value should be input at initialize stage
+    uint8_t resultCode;     // 0; success, 4; bus info none
+    uint8_t predictTime1;   // 1st bus arrival predict time ('1' means the bus gone)
+    uint8_t predictTime2;   // 2st bus arrival predict time ('1' means the none of next bus info)
+    uint8_t remainSeatCnt1; // 1st bus remain seat count
+    uint8_t remainSeatCnt2; // 2st bus remain seat count    
+}BUSINFO;
+
+////////////////////////////////////////////////////////////////
+// Function declaration
+////////////////////////////////////////////////////////////////
+void BuzzerSound(void);                     // Buzzer
+void Display_BusNumber(uint8_t * busnum);   // 4-digit display
+uint8_t get_businfo(BUSINFO * bus);         // Get Bus information (HTTP client) 
+
+// OLED display
+void Draw_OLED_init(void);
+void Draw_OLED_default(void);
+void Draw_OLED_busInfo(uint8_t * busnum, uint8_t seats);
+void Draw_OLED_arrivalMin(uint8_t min);
+void Draw_OLED_nextMin(uint8_t nextmin);
+void Draw_OLED_busList(void);
+
+// for ticker
+void beat();
+
+////////////////////////////////////////////////////////////////
+// Global variable declaration
+////////////////////////////////////////////////////////////////
+BUSINFO businfo[MAX_BUS_INFO];
+uint8_t flag_display_mode[MAX_BUS_INFO+1] = {0, };
+uint8_t display_mode = 0;
+uint8_t buzzer_sound_enable = 0;
+
+time_t beat_cTime;
+struct tm *beat_localtime;
+
+uint8_t mac[6] = {0x00,0x08,0xDC,0x01,0x02,0x03};   // MAC address, It must be modified as users MAC address
+
+int main()
+{   
+    pc.baud(115200);   
+    EthernetInterface ethernet;   
+    
+    uint8_t i;
+        
+    ////////////////////////////////////////////////////////////////
+    // Realtime Bus Information: Initialization
+    ////////////////////////////////////////////////////////////////     
+    
+    /* Bus number and route ID */
+    uint8_t busnum_1117[4] = {1, 1, 1, 7};
+    uint32_t busnum_1117_routeId = 234000069;
+    
+    uint8_t busnum_1113[4] = {1, 1, 1, 3};
+    uint32_t busnum_1113_routeId = 234000042;
+    
+    uint8_t busnum_1005[4] = {1, 0, 0, 5};
+    uint32_t busnum_1005_routeId = 234000065;
+    
+    // Bus info structure initialize    
+    businfo[0].busNum = busnum_1117;
+    businfo[0].stationNum = DEFAULT_STATION_NUM;
+    businfo[0].routeId = busnum_1117_routeId;
+    businfo[0].stationId = DEFAULT_STATION_ID;    
+    
+    businfo[1].busNum = busnum_1113;    
+    businfo[1].stationNum = DEFAULT_STATION_NUM;
+    businfo[1].routeId = busnum_1113_routeId;    
+    businfo[1].stationId = DEFAULT_STATION_ID;
+    
+    businfo[2].busNum = busnum_1005;    
+    businfo[2].stationNum = DEFAULT_STATION_NUM;
+    businfo[2].routeId = busnum_1005_routeId;    
+    businfo[2].stationId = DEFAULT_STATION_ID;
+    
+    // OLED display initialize
+    SeeedGrayOled.init();             // Initialize SEEED OLED display
+    SeeedGrayOled.clearDisplay();     // Clear Display
+    SeeedGrayOled.setNormalDisplay(); // Set Normal Display Mode
+    
+    // OLED display initial draw     
+    Draw_OLED_default();    //Draw_OLED_init();
+    
+    printf("\r\n==========================================\r\n");
+    printf(" Real-time Bus Arrival Alarm\r\n");
+    printf("==========================================\r\n");
+    printf(" Buzzer Sound START Time: %d:%.2d\r\n", SOUND_START_HOUR, SOUND_START_MIN);
+    printf(" Buzzer Sound END Time  : %d:%.2d\r\n", SOUND_END_HOUR, SOUND_END_MIN);
+    printf("==========================================\r\n");
+    
+    // Ethernet initialize
+    int ret = ethernet.init(mac);    
+    printf("\r\nWIZwiki-W7500 Networking Started\r\n");
+    wait(1); // 1 second for stable state
+
+    if (!ret) {
+        printf("Initialized, MAC: %s\r\n", ethernet.getMACAddress());
+        ret = ethernet.connect();
+        if (!ret) {
+            printf("IP: %s, MASK: %s, GW: %s\r\n",
+                      ethernet.getIPAddress(), ethernet.getNetworkMask(), ethernet.getGateway());
+        } else {
+            printf("Error ethernet.connect() - ret = %d\r\n", ret);
+            exit(0);
+        }
+    } else {
+        printf("Error ethernet.init() - ret = %d\r\n", ret);
+        exit(0);
+    }
+    
+    // NTP initialize
+    NTPClient ntp;    
+    printf("\r\nTrying to update time...\r\n");    
+    if (ntp.setTime("211.233.40.78") == 0) 
+    {
+        printf("Set time successfully\r\n");
+        time_t ctTime;
+        ctTime = time(NULL);
+        ctTime += 32400; // GMT+9/Seoul
+        printf("Time is set to (GMT+9): %s\r\n", ctime(&ctTime));
+    }
+    else
+    {
+        printf("Error\r\n");
+    }
+    
+    // 4-Digit display initialize
+    display.write(0, 0);
+    display.write(1, 0);
+    display.write(2, 0);
+    display.write(3, 0);
+    display.setColon(true);
+    tick.attach(&beat, 0.5);
+    
+    // Initial flag setting for get the bus info
+    flag_display_mode[DISPLAY_TIME_MODE] = 1;
+    
+    printf(">> Initialize Done\r\n");    
+    
+    // Main routine
+    while(1) {
+        
+        if(display_mode == DISPLAY_TIME_MODE) // Display: Time mode
+        {
+            if(flag_display_mode[display_mode])
+            {   
+                Draw_OLED_default();   
+                flag_display_mode[display_mode] = 0;
+                
+                // Get the buses info: Bus arrival time preparation (beforehand)
+                for(i = 0; i < MAX_BUS_INFO; i++)
+                {
+                    get_businfo(&businfo[i]);                                    
+                    
+#if (_DEBUG_BUS_ARRIVAL_ALARM_) //Debug message
+                    printf("Businfo #%d\r\n", i);
+                    printf("bus.resultCode = %c\r\n", businfo[i].resultCode);
+                    printf("1st Bus arrive after %d min\r\n", businfo[i].predictTime1);    
+                    printf("1st Bus remain seats %d\r\n", businfo[i].remainSeatCnt1);
+                    printf("2nd Bus arrive after %d min\r\n", businfo[i].predictTime2);
+                    printf("2nd Bus remain seats %d\r\n\r\n", businfo[i].remainSeatCnt2);
+#endif
+                }
+            }
+        }
+        else // Display: Bus info mode
+        {
+            i = display_mode - 1;
+            if(flag_display_mode[display_mode])
+            {                   
+                Draw_OLED_busInfo(businfo[i].busNum, businfo[i].remainSeatCnt1);
+                Draw_OLED_arrivalMin(businfo[i].predictTime1);
+                if(businfo[i].predictTime2 == 1)
+                {
+                    Draw_OLED_nextMin(0);
+                }
+                else
+                {
+                    Draw_OLED_nextMin(businfo[i].predictTime2);   
+                }
+                
+                // Buzzer sound!
+                // Each case means the displayed time(Remaining time until the bus arrival, minute) to ringing the buzzer
+                switch(businfo[i].predictTime1)
+                {
+                    case 5:
+                    case 6:
+                    case 7:                                                
+                    case 8:
+                    case 9:                        
+                    case 10:                                         
+                        BuzzerSound();                       
+                        break;
+                    default:
+                        break;
+                }
+                
+                flag_display_mode[display_mode] = 0;                
+            }
+        }       
+    }
+}
+
+////////////////////////////////////////////////////////////////
+// Member function of Ticker interface (attaching to a ticker)
+////////////////////////////////////////////////////////////////
+void beat()
+{
+    static uint8_t colon = 0;
+    static uint8_t myled_blink = 1;
+    static uint8_t display_mode_change_cnt = 0; // initial count value; 5 sec reduced    
+        
+    // Get current local time info
+    beat_cTime = time(NULL);
+    beat_cTime += 32400; // GMT+9/Seoul    
+    beat_localtime = localtime(&beat_cTime);    
+    
+    // Display Mode change handler (Time / Bus mode)
+    display_mode_change_cnt++; // ++ every 0.5sec
+    if((display_mode_change_cnt/2) >= DISPLAY_MODE_CHANGE_CNT_SEC)
+    {
+        display_mode++;        
+        
+        if(display_mode > MAX_BUS_INFO)
+        {
+            display_mode = DISPLAY_TIME_MODE; // 0
+        }
+         
+        // Skip: (resultCode == 4) means 'Bus arrival info does not appear'
+        while((display_mode != DISPLAY_TIME_MODE) && (businfo[display_mode-1].resultCode == 4))
+        {
+            display_mode++;
+            
+            if(display_mode > MAX_BUS_INFO)
+            {
+                display_mode = DISPLAY_TIME_MODE; // 0
+            }
+        }        
+        
+        flag_display_mode[display_mode] = 1;
+        display_mode_change_cnt = 0;
+    }
+    
+    // 4-Digit Display handler
+    if(display_mode == DISPLAY_TIME_MODE) // Time mode
+    {
+        display.on();
+        display.setColon(colon);    
+        if (colon) {                             
+            display.write(0, beat_localtime->tm_hour / 10);
+            display.write(1, beat_localtime->tm_hour % 10);        
+            display.write(2, beat_localtime->tm_min / 10);
+            display.write(3, beat_localtime->tm_min % 10);            
+        }            
+        //colon = 1 - colon;  // moved below     
+    }
+    else // Bus info mode                
+    {       
+#if (BUS_NUM_BLINK_ENABLE)
+        if(colon)
+        {        
+            Display_BusNumber(businfo[display_mode-1].busNum);
+        }
+        else
+        {
+            Display_BusNumber(NULL);
+        }
+#else
+        Display_BusNumber(businfo[display_mode-1].busNum);
+#endif
+           
+    }
+    
+    // Buzzer sound enable handler 
+    buzzer_sound_enable = 0;
+#if (ALARM_SOUND_ENABLE)
+    if(((beat_localtime->tm_hour == SOUND_START_HOUR) && (beat_localtime->tm_min >= SOUND_START_MIN)) || (beat_localtime->tm_hour > SOUND_START_HOUR))
+    {
+        if((beat_localtime->tm_hour < SOUND_END_HOUR) || ((beat_localtime->tm_hour == SOUND_END_HOUR) && (beat_localtime->tm_min < SOUND_END_MIN)))
+        {
+            buzzer_sound_enable = 1;
+        }
+    }
+#endif
+    
+    // LED blink; Device working indicator
+    myled_blink ^= 1;
+    myled = myled_blink ;
+    
+    // Invert every 0.5sec
+    colon = 1 - colon;
+}
+
+////////////////////////////////////////////////////////////////
+// Get Bus Information (HTTP client) Functions
+////////////////////////////////////////////////////////////////
+uint8_t get_businfo(BUSINFO * bus)
+{
+    uint8_t ret = 0;
+    char str[2048] = {0, };
+    char get_req[512] =  {0, };    
+    char predictTime1[3], predictTime2[3];
+    char remainSeatCnt1[3], remainSeatCnt2[3];    
+    char *CurrentAddr = 0;
+    
+    //uint8_t retry_cnt = 0;            // Unimplemented, MAX_HTTPC_RETRY_CNT
+    
+    HTTPClient httpc;
+    
+    sprintf(get_req, "http://openapi.gbis.go.kr/ws/rest/busarrivalservice?serviceKey=test&routeId=%ld&stationId=%ld", bus->routeId, bus->stationId);    
+    httpc.get(get_req, str, sizeof(str), 1000);
+    
+    CurrentAddr = strstr(str,"<resultCode>");
+    bus->resultCode = *(CurrentAddr+12);  
+        
+    if(bus->resultCode == '0')
+    {    
+        CurrentAddr = strstr(str,"<predictTime1>");
+        if((*(CurrentAddr+15)) == '<')
+        {
+            predictTime1[0] = *(CurrentAddr+14);
+            predictTime1[1] = 0;
+            predictTime1[2] = 0;
+        }
+        else
+        {
+            predictTime1[0] = *(CurrentAddr+14);
+            predictTime1[1] = *(CurrentAddr+15);
+            predictTime1[2] = 0;
+        }
+        bus->predictTime1 = (uint8_t)atoi(predictTime1);
+            
+        CurrentAddr = strstr(str,"<predictTime2>");
+        if((*(CurrentAddr+15)) == '<')
+        {
+            predictTime2[0] = *(CurrentAddr+14);
+            predictTime2[1] = 0;
+            predictTime2[2] = 0;
+        }
+        else
+        {
+            predictTime2[0] = *(CurrentAddr+14);
+            predictTime2[1] = *(CurrentAddr+15);
+            predictTime2[2] = 0;
+        }
+        bus->predictTime2 = (uint8_t)atoi(predictTime2);
+        
+        CurrentAddr = strstr(str,"<remainSeatCnt1>");
+        if((*(CurrentAddr+17)) == '<')
+        {
+            remainSeatCnt1[0] = *(CurrentAddr+16);
+            remainSeatCnt1[1] = 0;
+            remainSeatCnt1[2] = 0;
+        }
+        else
+        {
+            remainSeatCnt1[0] = *(CurrentAddr+16);
+            remainSeatCnt1[1] = *(CurrentAddr+17);
+            remainSeatCnt1[2] = 0;
+        }
+        bus->remainSeatCnt1 = (uint8_t)atoi(remainSeatCnt1);
+        
+        if(bus->predictTime2 > 1)   // (predictTime2 == 1) => The next bus information is not confirmed.
+        {        
+            CurrentAddr = strstr(str,"<remainSeatCnt2>");
+            if((*(CurrentAddr+17)) == '<')
+            {
+                remainSeatCnt2[0] = *(CurrentAddr+16);
+                remainSeatCnt2[1] = 0;
+                remainSeatCnt2[2] = 0;
+            }
+            else
+            {
+                remainSeatCnt2[0] = *(CurrentAddr+16);
+                remainSeatCnt2[1] = *(CurrentAddr+17);
+                remainSeatCnt2[2] = 0;
+            }
+            bus->remainSeatCnt2 = (uint8_t)atoi(remainSeatCnt2);
+        }
+        
+        ret = 1;
+    }
+    
+    return ret;
+}
+
+////////////////////////////////////////////////////////////////
+// Piezo Buzzer Functions
+////////////////////////////////////////////////////////////////
+
+void playNote(float frequency, float duration, float volume) 
+{
+    Buzzer.period(1.0/(double)frequency);
+    Buzzer = ((double)volume/2.0);
+    wait(duration);
+    Buzzer = 0.0;
+}
+
+void BuzzerSound(void)
+{
+    // Calculate duration of a quarter note from bpm
+    float beat_duration = 60.0 / BPM;    
+
+    if(buzzer_sound_enable)
+    {
+        //playNote(329.628, (0.75 * (double)beat_duration), VOLUME);
+        //playNote(261.626, (0.75 * (double)beat_duration), VOLUME);
+        playNote(529.628, (0.50 * (double)beat_duration), VOLUME);
+        playNote(301.626, (0.50 * (double)beat_duration), VOLUME);
+    }
+}
+
+////////////////////////////////////////////////////////////////
+// 4-Digit Display Functions
+////////////////////////////////////////////////////////////////
+
+void Display_BusNumber(uint8_t * busnum)
+{       
+    display.setColon(false);
+    
+    if(busnum != NULL)
+    {
+        display.on();
+        display.write(0, busnum[0]);
+        display.write(1, busnum[1]);
+        display.write(2, busnum[2]);
+        display.write(3, busnum[3]);
+    }
+    else
+    {
+        display.off();    
+    }
+}
+
+////////////////////////////////////////////////////////////////
+// OLED Display Functions
+////////////////////////////////////////////////////////////////
+
+void Draw_OLED_init(void)
+{
+    uint8_t empty_busnum[4] = {0, 0, 0, 0};
+    
+    Draw_OLED_busInfo(empty_busnum, 0);
+    Draw_OLED_nextMin(0);    
+}
+
+void Draw_OLED_default(void)
+{
+    SeeedGrayOled.setTextXY(0, 1);
+    SeeedGrayOled.putString("Bus NearBy ");
+    SeeedGrayOled.setTextXY(1, 1);    
+    SeeedGrayOled.putString("========== ");
+    
+    Draw_OLED_arrivalMin('b');
+    
+    SeeedGrayOled.setTextXY(11, 0);
+    SeeedGrayOled.putString("WIZnet::Eric");    
+}
+
+void Draw_OLED_busList(void)
+{
+    uint8_t i;
+    char buf[15] = {0, };
+    
+    SeeedGrayOled.setTextXY(0, 1);
+    SeeedGrayOled.putString("           ");
+    SeeedGrayOled.setTextXY(1, 1);    
+    SeeedGrayOled.putString("Bus List:");
+    
+    Draw_OLED_arrivalMin('t');   
+    
+    for(i = 0; i < MAX_BUS_INFO; i++)
+    {        
+        SeeedGrayOled.setTextXY(3+i, 2);
+        sprintf(buf, "- %.1d%.1d%.1d%.1d", businfo[i].busNum[0], businfo[i].busNum[1], businfo[i].busNum[2], businfo[i].busNum[3]);
+        SeeedGrayOled.putString(buf);
+    }
+    
+    SeeedGrayOled.setTextXY(11, 1);
+    SeeedGrayOled.putString("           ");
+}
+
+void Draw_OLED_busInfo(uint8_t * busnum, uint8_t seats)
+{
+    char buf[15] = {0, };
+    sprintf(buf, "Bus  %.1d%.1d%.1d%.1d ", busnum[0], busnum[1], busnum[2], busnum[3]);    
+    SeeedGrayOled.setTextXY(0, 1);
+    SeeedGrayOled.putString(buf);
+    sprintf(buf, "Seats  %.2d ", seats);    
+    SeeedGrayOled.setTextXY(1, 1);
+    SeeedGrayOled.putString(buf);    
+}
+
+void Draw_OLED_nextMin(uint8_t nextmin)
+{
+    char buf[15] = {0, };
+    if(nextmin == 0)
+    {
+        sprintf(buf, "Next: -- min");
+    }
+    else
+    {
+        sprintf(buf, "Next: %.2d min", nextmin);
+    }
+    
+    SeeedGrayOled.setTextXY(11, 0);
+    SeeedGrayOled.putString(buf);
+}
+
+void Draw_OLED_arrivalMin(uint8_t min)
+{
+    switch(min)
+    {
+         case 't':
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("            ");
+            break;
+        case 'b':
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("   ~~~~~   ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("  ~     ~  ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("  ~     ~  ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("  ~~~~~~~  ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("  ~ ~~~ ~  ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("  ~~~~~~~  ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("   ~~ ~~    ");
+            break;
+        case 0:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString(" ~  ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString(" ~  ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString(" ~  ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString(" ~  ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString(" ~  ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~  ");
+            break;
+        case 1:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("           ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("    ~      ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("   ~~      ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("  ~~~~~~~  ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("   ~~      ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("    ~      ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      Gone! ");
+            break;
+        case 2:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;
+        case 3:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;
+         case 4:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~ ~  ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~ ~  ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~ ~  ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("        ~  ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("        ~  ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("        ~   ");
+            break;
+        case 5:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;
+        case 6:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~    ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;
+        case 7:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("         ~  ");
+            break;
+        case 8:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;
+        case 9:
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("      ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("      ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("         ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("      ~~~~  ");
+            break;        
+        case 10:
+        case 11: // 10
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("  ~~  ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("   ~  ~~~~  ");
+            break;
+        case 12:
+        case 13:
+        case 14: // 12
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("  ~~  ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("   ~  ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("   ~  ~    ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("   ~  ~    ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("   ~  ~~~~  ");
+            break;
+        case 15:
+        case 16: // 15        
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("  ~~  ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("   ~  ~    ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("   ~  ~    ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("   ~  ~~~~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("   ~  ~~~~  ");
+            break;
+        case 17:
+        case 18: // 17
+        case 19: // 17
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString("  ~~  ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString("   ~  ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString("   ~     ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString("   ~     ~  ");
+            break;
+        case 20: // 20
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("    ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("    ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString(" ~~~~ ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString(" ~    ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString(" ~    ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~  ");
+            break;
+        default: // 20
+            SeeedGrayOled.setTextXY(3, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~ ");
+            SeeedGrayOled.setTextXY(4, 0);
+            SeeedGrayOled.putString("    ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(5, 0);
+            SeeedGrayOled.putString("    ~ ~  ~ ");
+            SeeedGrayOled.setTextXY(6, 0);
+            SeeedGrayOled.putString(" ~~~~ ~  ~ ");
+            SeeedGrayOled.setTextXY(7, 0);
+            SeeedGrayOled.putString(" ~    ~  ~ ");
+            SeeedGrayOled.setTextXY(8, 0);
+            SeeedGrayOled.putString(" ~    ~  ~ ");
+            SeeedGrayOled.setTextXY(9, 0);
+            SeeedGrayOled.putString(" ~~~~ ~~~~ ~");
+            break;
+    }
+}
\ No newline at end of file