├── README.md └── WristWatch_M5.ino /README.md: -------------------------------------------------------------------------------- 1 | The days are passing by, searching for a good IoT platform that is Opensource and seems something like a wearable, and here it is. Behold, the all new #M5StickC. 2 | I am a DIY Tinkerer, love to work developing different cool projects with smart features. Everyone loves when something physically smart is in their hand. 3 | But since quite a long time, I had been searching for an open source hardware, which should have all the features a smart watch used to has. I searched all over the internet, but could not be able to find one. There are many & many of hardware available, but those are either not open source for development, or very much expensive for a DIY geek like me. 4 | But one beautiful day, I saw this stunning opensource IoT platform. Yes, none other, but #M5StickC. 5 | This finger sized hardware has built in color OLED 0.96" display, for displaying alphanumeric characters, graphs, logos, animations, and even images. It has an 80mAh battery inside, with USB Type-C charger. An ESP32 pico chip allows a very fast processor with 4MB Flash, WiFi and BLE connectivity options. Also, there is a 6-axis Gyroscope+Accelerometer inside. You will be astonished to know that there is a small mic, an IR LED, RED LED and 3 buttons for user experience, all stuffed with this small gadget. 6 | For prototyping of your ideas, there are 5 GPIOs for externally connecting sensors, actuators, motors, and displays. 7 | For simplicity of use, there are bunch of libraries, examples and other cool stuff all open source. So, from now, if you're interested in developing your own Wearable gadget, which works according to your requirements, all you need is an #M5StickC. 8 | Now come the pricing part. The cheap priced platform is available from their website, and also can be found on AliExpress. You can have the device for only US $9.95. Links for these are given below. 9 | 10 | Website: https://m5stack.com/ 11 | AliExpress: https://www.aliexpress.com/item/32985247364.html?spm=a2g0o.productlist.0.0.367f6efaE2hcNm&algo_pvid=6bd51197-65f3-4774-9090-77af5ef6fe2e&algo_expid=6bd51197-65f3-4774-9090-77af5ef6fe2e-11&btsid=d0f3bbe0-dfe6-48a8-913c-d804a72bfb51&ws_ab_test=searchweb0_0,searchweb201602_4,searchweb201603_53 12 | 13 | Now comes the fun part. The device supports programming platforms like ArduinoIDE, UIFlow (Blockly, MicroPython). 14 | After some tinkering and getting myself familiar with the platform and libraries, I decided to make a wristwatch out of this Stick. 15 | The code snippets helped me a lot in developing my first prototype. And the example code of TFT_Clock is worth appreciating. 16 | After uploading and testing the code, I came to know that every time I reset the device, it start the clock with the time I uploaded the code. 17 | Good thing, this finger-sized computer has a built-in RTC. Then again, the example code helped me to use the concept of Real Time Clock, which solved my problem of clock time reset. But the thing to remember in the code that I have written, is that, you have to comment some lines after first upload, so that it does not reprogram the RTC every time, which is not required afterwards. 18 | Then come the battery drainage problem. Every time the clock is charged to full level, the battery drains very quickly. I am very excited to share, that this tiny little thing also has a Power Management IC AXP192 in it, which controls the power consumption of the #M5StickC. So I decided to reduce the power consumption of my wrist watch with this IC. The example codes have good example about using Sleep functions with this IoT device. According to the documentations, the device consumes less than 10uA of current when in Deep Sleep state. One thing that was not clear in the examples, is that how to wake up the device from deep sleep. 19 | Let me give you some concept of this. When in Deep Sleep, all parts of the device sleeps, including sensors I/Os, peripherals, etc. But what is still active, is the RTC peripheral buttons for triggering up the device from deep sleep. And the good part is, all the buttons on the #M5StickC are RTC trigger buttons. In my code, I have used the M5_Home_Button for waking up the device from Deep Sleep. 20 | For better battery consumption controlling, I first controlled the brightness of the OLED Display. I used built in accelerometer to detect when the watch is front of my face. So every time I lift my arm to see the time, the screen light automatically turns ON. And when I lower my arm, the light automatically turns OFF. So now, I saved much battery time in this way. 21 | The next thing I have done, is when up to some time (10s), when I am not watching for the time, the device enters the DEEP SLEEP. In this way, the battery consumption drops to nearly 0 (<10uA). When I want to see the time, I just press the home button, and the wristwatch starts within a second, and I can see the time. 22 | To make myself aware of the battery percentage, I used example code for checking the Battery voltages, and converted them into percentage. The display always shows me the amount of battery power remaining. 23 | Summary: 24 | With the help of this finger-sized computer, #M5StickC, I have made my own wrist watch, with auto brightness, and DEEP SLEEP functions to enhance the power up time of my watch. 25 | That's all for this project, hope you like it. :-) 26 | -------------------------------------------------------------------------------- /WristWatch_M5.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | RTC_TimeTypeDef RTC_TimeStruct; 4 | RTC_DateTypeDef RTC_DateStruct; 5 | 6 | 7 | uint8_t led_count = 15; 8 | long brightnessTime, tiltTime = 0, tiltTime2 = 0; 9 | float b, c = 0; 10 | int battery = 0; 11 | 12 | float accX = 0; 13 | float accY = 0; 14 | float accZ = 0; 15 | 16 | 17 | #define TFT_GREY 0x5AEB 18 | 19 | float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0; // Saved H, M, S x & y multipliers 20 | float sdeg = 0, mdeg = 0, hdeg = 0; 21 | uint16_t osx = 120, osy = 120, omx = 120, omy = 120, ohx = 120, ohy = 120; // Saved H, M, S x & y coords 22 | uint16_t x0 = 0, x1 = 0, yy0 = 0, yy1 = 0; 23 | uint32_t targetTime = 0; // for next 1 second timeout 24 | 25 | static uint8_t conv2d(const char* p); // Forward declaration needed for IDE 1.6.x 26 | 27 | uint8_t hh, mm, ss, YY, MM, DD, dd; 28 | 29 | boolean initial = 1; 30 | 31 | void setup() { 32 | //COMMENT next line after first upload 33 | 34 | M5.begin(); 35 | Wire.begin(32, 33); 36 | pinMode(M5_BUTTON_HOME, INPUT); 37 | pinMode(M5_BUTTON_RST, INPUT); 38 | pinMode(M5_BUTTON_HOME, INPUT_PULLUP); 39 | 40 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_37, 0); //1 = High, 0 = Low 41 | 42 | //COMMENT next 12 lines after first upload 43 | 44 | hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time 45 | RTC_TimeTypeDef TimeStruct; 46 | TimeStruct.Hours = hh; 47 | TimeStruct.Minutes = mm; 48 | TimeStruct.Seconds = ss; 49 | M5.Rtc.SetTime(&TimeStruct); 50 | RTC_DateTypeDef DateStruct; 51 | DateStruct.WeekDay = 1; 52 | DateStruct.Month = 9; 53 | DateStruct.Date = 22; 54 | DateStruct.Year = 2019; 55 | M5.Rtc.SetData(&DateStruct); 56 | 57 | //COMMENT UPTO HERE 58 | 59 | M5.Rtc.GetTime(&RTC_TimeStruct); 60 | hh = RTC_TimeStruct.Hours; 61 | mm = RTC_TimeStruct.Minutes; 62 | ss = RTC_TimeStruct.Seconds; 63 | 64 | M5.Rtc.GetData(&RTC_DateStruct); 65 | YY = RTC_DateStruct.Year; 66 | MM = RTC_DateStruct.Month; 67 | DD = RTC_DateStruct.Date; 68 | dd = RTC_DateStruct.WeekDay; 69 | 70 | M5.Lcd.setRotation(3); 71 | M5.Lcd.fillScreen(TFT_GREY); 72 | 73 | M5.Lcd.setTextColor(TFT_WHITE, TFT_GREY); // Adding a background colour erases previous text automatically 74 | 75 | // Draw clock face 76 | M5.Lcd.fillCircle(40, 40, 40, TFT_BLACK); 77 | M5.Lcd.fillCircle(40, 40, 36, TFT_BLACK); 78 | 79 | // Draw 12 lines 80 | for (int i = 0; i < 360; i += 30) { 81 | sx = cos((i - 90) * 0.0174532925); 82 | sy = sin((i - 90) * 0.0174532925); 83 | x0 = sx * 38 + 40; 84 | yy0 = sy * 38 + 40; 85 | x1 = sx * 32 + 40; 86 | yy1 = sy * 32 + 40; 87 | 88 | M5.Lcd.drawLine(x0, yy0, x1, yy1, TFT_WHITE); 89 | } 90 | 91 | // Draw 60 dots 92 | for (int i = 0; i < 360; i += 6) { 93 | sx = cos((i - 90) * 0.0174532925); 94 | sy = sin((i - 90) * 0.0174532925); 95 | x0 = sx * 34 + 40; 96 | yy0 = sy * 34 + 40; 97 | // Draw minute markers 98 | M5.Lcd.drawPixel(x0, yy0, TFT_WHITE); 99 | 100 | // Draw main quadrant dots 101 | if (i == 0 || i == 180) M5.Lcd.fillCircle(x0, yy0, 2, TFT_WHITE); 102 | if (i == 90 || i == 270) M5.Lcd.fillCircle(x0, yy0, 2, TFT_WHITE); 103 | } 104 | 105 | M5.Lcd.fillCircle(40, 40, 2, TFT_WHITE); 106 | M5.Lcd.drawCentreString("WAyKEy", 120, 260, 4); 107 | 108 | targetTime = millis() + 1000; 109 | 110 | M5.Axp.ScreenBreath(9); 111 | M5.MPU6886.Init(); 112 | 113 | } 114 | void brightnessT() { 115 | M5.MPU6886.getAccelData(&accX, &accY, &accZ); 116 | accX *= 1000; 117 | accY *= 1000; 118 | 119 | if ((accX < (0 - 200) && (accX > -900)) && ((accY > 0 - 300) && (accY < 300))) 120 | { 121 | if (millis() > (tiltTime + 500)) { 122 | M5.Axp.ScreenBreath(12); 123 | brightnessTime = millis(); 124 | // while (accX < (0 - 500) && ((accY > 0) && (accY < 100))); 125 | } 126 | } 127 | else 128 | tiltTime = millis(); 129 | 130 | if (brightnessTime < millis() - 2000) 131 | { 132 | M5.Axp.ScreenBreath(7); 133 | brightnessTime = 0; 134 | } 135 | } 136 | void wristWatch() { 137 | if (targetTime < millis()) { 138 | targetTime += 1000; 139 | ss++; // Advance second 140 | if (ss == 60) { 141 | ss = 0; 142 | mm++; // Advance minute 143 | if (mm > 59) { 144 | mm = 0; 145 | hh++; // Advance hour 146 | if (hh > 23) { 147 | hh = 0; 148 | } 149 | } 150 | } 151 | 152 | // Pre-compute hand degrees, x & y coords for a fast screen update 153 | sdeg = ss * 6; // 0-59 -> 0-354 154 | mdeg = mm * 6 + sdeg * 0.01666667; // 0-59 -> 0-360 - includes seconds 155 | hdeg = hh * 30 + mdeg * 0.0833333; // 0-11 -> 0-360 - includes minutes and seconds 156 | hx = cos((hdeg - 90) * 0.0174532925); 157 | hy = sin((hdeg - 90) * 0.0174532925); 158 | mx = cos((mdeg - 90) * 0.0174532925); 159 | my = sin((mdeg - 90) * 0.0174532925); 160 | sx = cos((sdeg - 90) * 0.0174532925); 161 | sy = sin((sdeg - 90) * 0.0174532925); 162 | 163 | if (ss == 0 || initial) { 164 | initial = 0; 165 | // Erase hour and minute hand positions every minute 166 | M5.Lcd.drawLine(ohx, ohy, 40, 40, TFT_BLACK); 167 | ohx = hx * 15 + 40; 168 | ohy = hy * 15 + 40; 169 | M5.Lcd.drawLine(omx, omy, 40, 40, TFT_BLACK); 170 | omx = mx * 20 + 40; 171 | omy = my * 20 + 40; 172 | } 173 | 174 | // Redraw new hand positions, hour and minute hands not erased here to avoid flicker 175 | M5.Lcd.drawLine(osx, osy, 40, 40, TFT_BLACK); 176 | osx = sx * 25 + 40; 177 | osy = sy * 25 + 40; 178 | M5.Lcd.drawLine(osx, osy, 40, 40, TFT_RED); 179 | M5.Lcd.drawLine(ohx, ohy, 40, 40, TFT_WHITE); 180 | M5.Lcd.drawLine(omx, omy, 40, 40, TFT_WHITE); 181 | M5.Lcd.drawLine(osx, osy, 40, 40, TFT_RED); 182 | 183 | M5.Lcd.fillCircle(40, 40, 2, TFT_RED); 184 | } 185 | dd = RTC_DateStruct.WeekDay; 186 | weekDay(); 187 | Date(); 188 | } 189 | 190 | void weekDay() { 191 | M5.Lcd.setCursor(110, 12, 2); 192 | M5.Lcd.setTextColor(WHITE, TFT_GREY); 193 | 194 | // M5.Lcd.print(dd); 195 | switch (dd) { 196 | case 1: 197 | M5.Lcd.print("SUN"); 198 | break; 199 | case 2: 200 | M5.Lcd.print("MON"); 201 | break; 202 | case 3: 203 | M5.Lcd.print("TUE"); 204 | break; 205 | case 4: 206 | M5.Lcd.print("WED"); 207 | break; 208 | case 5: 209 | M5.Lcd.print("THU"); 210 | break; 211 | case 6: 212 | M5.Lcd.print("FRI"); 213 | break; 214 | case 0: 215 | M5.Lcd.print("SAT"); 216 | break; 217 | } 218 | } 219 | 220 | void Date() { 221 | M5.Lcd.setCursor(110, 26, 2); 222 | M5.Lcd.setTextColor(WHITE, TFT_GREY); 223 | M5.Lcd.print(DD); 224 | M5.Lcd.print('/'); 225 | M5.Lcd.print(MM); 226 | } 227 | 228 | void batteryLevel() { 229 | M5.Lcd.setCursor(110, 3, 1); 230 | c = M5.Axp.GetVapsData() * 1.4 / 1000; 231 | b = M5.Axp.GetVbatData() * 1.1 / 1000; 232 | // M5.Lcd.print(b); 233 | battery = ((b - 3.0) / 1.2) * 100; 234 | 235 | if (c >= 4.5) { 236 | M5.Lcd.setTextColor(TFT_YELLOW, TFT_GREY); 237 | M5.Lcd.print("CHG:"); 238 | } 239 | else { 240 | M5.Lcd.setTextColor(TFT_GREEN, TFT_GREY); 241 | M5.Lcd.print("BAT:"); 242 | } 243 | 244 | if (battery > 100) 245 | battery = 100; 246 | else if (battery < 100 && battery > 9) 247 | M5.Lcd.print(" "); 248 | else if (battery < 9) 249 | M5.Lcd.print(" "); 250 | if (battery < 10) 251 | M5.Axp.DeepSleep(); 252 | 253 | // if (digitalRead(M5_BUTTON_HOME) == LOW) { 254 | // while (digitalRead(M5_BUTTON_HOME) == LOW); 255 | // M5.Axp.DeepSleep(SLEEP_SEC(1)); 256 | // } 257 | M5.Lcd.print(battery); 258 | M5.Lcd.print("%"); 259 | } 260 | 261 | void batterySaver() { 262 | M5.MPU6886.getAccelData(&accX, &accY, &accZ); 263 | accX *= 1000; 264 | accY *= 1000; 265 | 266 | if (!((accX < (0 - 200) && (accX > -900)) && ((accY > 0 - 300) && (accY < 300)))) 267 | { 268 | if (millis() > (tiltTime2 + 10000)) { 269 | M5.Axp.DeepSleep(); 270 | // while (accX < (0 - 500) && ((accY > 0) && (accY < 100))); 271 | } 272 | } 273 | else 274 | tiltTime2 = millis(); 275 | } 276 | void loop() { 277 | 278 | brightnessT(); 279 | 280 | 281 | wristWatch(); 282 | 283 | 284 | batteryLevel(); 285 | 286 | 287 | batterySaver(); 288 | } 289 | 290 | static uint8_t conv2d(const char* p) { 291 | uint8_t v = 0; 292 | if ('0' <= *p && *p <= '9') 293 | v = *p - '0'; 294 | return 10 * v + *++p - '0'; 295 | } 296 | --------------------------------------------------------------------------------