When I started to learn about Arduino everything was more complicated than now, on internet there was less information, less videos or tutorials, less kind of modules to buy on eBay, DealeXtreme, AliExpress or other webs.
It wasn’t a big problem for me because I have done these kinds of things for long time and I could find the answer I needed.
However, not everybody has the same knowledge or patience. Or not everybody wants or is able to spend a lot of time. Then, it’s good that now there are more companies and platforms to choose the best solution for each one.
Today, I’m going to talk about the Grove platform of SeeedStudio.
I knew SeeedStudio before because I used this company to make boards long time ago, if I remember well, I sent my 3rd board, which I designed, to them. Anyway, they don’t only manufacture boards, they sell their own boards as well.
In my second or third month in NZ I read from someone who published on twitter about the Seeeduino XIAO, this is a minuscule board with include a powerful microcontroller and USB-C as interface. I really wanted to have one, so, when this company contacted me to make a review for another board, I couldn’t say no, I accepted and I ordered as well the tiny board that I’m going to review in another article.
What is Grove platform of SeeedStudio?
Grove is a platform that allow beginners start with electronics boards, microcontrollers, modules, sensors, etc. without using any tool such as protoboards, cables, solder iron, etc. It’s possible thanks to these boards and modules have a connector to join with only one cable. Then, the beginner can focus only on programming code and less in electronic. It can be good for people who are studying at college about programming.
Seeeduino Grove Beginner Kit for Arduino
SeeedStudio has two versions of this board. The first one has everything separate and accompanied with a plastic box to save them. However, it’s the second version the best one in my opinion, because this version all is united at the same board and pre-wired, the meaning is that we can program and start to use it without using any cable. Of course, we are free to cut the union between boards and use these with cables.
This second version contains the main board and 10 modules as well. We’re going to see everything right now:
Box
I really thought the box would be bigger, but not, it’s small because in this board everything is small! The carton box show us the main characteristics like that the board is compatible with Arduino, it include 10 modules pre-wired, and 12 tutorials to use it (a PDF file with 71 pages to understand everything) that you can download from their website (here, or here to go to the wiki), you can download as well the Eagle files that contain the schematic and circuit and modify or copy it if you want.
Main board
The main board which will work like an Arduino has the same microcontroller than an Arduino Uno, but here, they have used an ATmega 328P with encapsulated MLF which is really small. The chip that allow to program this board by USB is the CP2102, it’s one of the most famous chips to convert the USB signal to Serial signal, so we shouldn’t have problems to connect it with our computer, and it’s possible that the computer installs the board automatically without us having to do anything. (You won’t have to do anything if you use Windows 10, your computer will install it automatically, I tried it.)
How we can see in the pictures, the board include the typical connectors to connect an expansion Arduino Shield (in yellow colour), or 12 white connectors where we can connect the modules using cables (this kit include 6 Grove cables) if we separate them from the panel.
Finally, the board include as well the reset button and some LEDs to indicate its status.
Pros:
-
- The quality of the board is indisputable, it looks perfect: good welds, clean, everything well centered, etc.
- Connectors differenced by colour, yellow for Arduino and white for Grove.
- Reset button with status led, when you touch the reset button a led turns on to indicate it.
- Microcontroller and chip Serial standers, easy to program with Arduino IDE.
Cons:
-
- I can’t understand why they have used a MicroUSB instead of USB-C, I have such a bad experiences with this connector in boards without a shell. They should use USB-C.
- The reset button can be difficult to push.
- This Arduino board doesn’t have jack to power it without USB.
Modules
This kit include 10 modules with which we can learn a lot because they are the most typical modules that we can use. If you need extra modules you can find them on SeeedStudio website (They have more than 100 modules/sensors).
LED – D4 (output module)
This module contain a LED connected to a socket. It allows us to change the LED if we want. The module includes a potentiometer to modify the voltage that the LED receives. The D4 pin, where it’s connected, isn’t compatible with PWM, so we can not modify the brightness by Arduino.
Pros: LED interchangeable, brightness dimmable by hardware.
Cons: It doesn’t support dimmable by software.
(if we separate the module from the board, maybe we can connect it to another connector with PWM, but I don’t want to separate, so I’m not sure if this will work)
Buzzer – D5 (output module)
There are two kinds of buzzers, active or passive. The first one we can turn on/off and this generates sound itself, however for the second one, the passive, we need to generate the sound. This module contain a passive buzzer, so we can generate different sounds using a PWM pin. The module is pre-wired with the D5 pin which supports PWM.
Pros: It can play so many kinds of sounds by software.
Cons: Nothing.
OLED Display – I2C (output module)
This module includes a white OLED display with a size of 0.96″ and a resolution of 128×64. It’s pre-wired to the main board using the I2C interface. The I2C default address for this module is 0x78 but we can change to 0x7A with the SMD jumper that the module includes. (cutting the wired and soldering the pads, but we don’t need to do it except if we have another module using the same address, not with this kit)
Pros: It’s possible to change the I2C address.
Cons: It’s not a colour display and doesn’t have SMD pads. *
Button – D6 (input module)
This module is a simple button, we can push it and read from Arduino when it’s pulsing. It’s connected to the D6 pin.
Pros: Nothing
Cons: We can’t use external interruptions because the D6 pin doesn’t support it. (We can separate the board and connect it using a cable). It doesn’t have SMD pads. *
Rotatory potentiometer – A0 (input module)
This another module contain a rotatory potentiometer, we can read a value between 0 to 1023. It’s a simple potentiometer, not an encoder, so we can not spin it 360º. It’s connected to the A0 pin.
Pros: Nothing
Cons: I think the module should have had a rotary encoder, not a potentiometer. A rotary encoder is more functional to control a screen menu.
Light – A6 (input module)
This is another anagogic module. In this case this module can get the level of light, with the example sketch that the Arduino has preprogramed the sensor read a value between 0 to 748. It’s connected to the A6 pin.
Pros: Nothing
Cons: Nothing
Sound – A2 (input module)
This module is able to detect sounds using the anagogic A2 pin. For example we can use it to turn on/off a light using a clap.
Pros: Nothing
Cons: Nothing
Temperature and Humidify – D3 (input module)
It’s one of the most typical sensors to detect the temperature and humidity that we can find on internet. It’s a good sensor but its older brother is most sensitive than this. It’s connected to the D3 pint.
Pros: Nothing
Cons: The sensor that this module has is the DHT11, they should have put the DHT22 because is more sensitive an precise, and this is not to much expensive.
Air pressure – I2C (input module)
This board include a barometer which is a scientific instrument that is used to measure air pressure in a certain environment. It’s connected using the I2C bus.
Pros: Nothing
Cons: It isn’t possible to change the I2C address. It doesn’t have SMD pads. *
3-Axis Acceleration (input module)
This is one of the most interesting modules in this kit after the OLED screen, with the demo we can move a ball on the screen. This module is connected using the I2C bus.
Pros: Nothing
Cons: This sensor is soldered on the board in the wrong position, if you check the picture you can see how the axis X and Y are inverted between them. It can be fixed by software.
It isn’t possible to change the I2C address.
* All boards except the button, OLED, and air pressure have SMD pads on the bottom layer, it can be interesting if we want to solder cables or the board directly to another board. I checked the Eagle schematic file and each board has been designed/checked by different person, so I think the three boards don’t have these pads because someone forgot to put it. I can’t imagine another reason.
Code
I want to share the code that I made. I used the original code that the board include and I have improved it as much as I could. Now, the sketch include an example per each sensor/module in the board, so the sketch include 10 demonstrations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 |
/** * Name: Grove Beginner Kit for Arduino, example code * Autor: Alberto Gil Tesa * Web: https://giltesa.com/?p=20129 * License: CC BY-NC-SA 4.0 * Version: 1.0 * Date: 2020/08/29 * * https://gist.github.com/giltesa/78ca196e30294fe0389192ea9067dcde */ //LIBRARIES // #include <Wire.h> #include <U8g2lib.h> #include <DHT.h> #include <Seeed_BMP280.h> #include <LIS3DHTR.h> //#define DEBUG //MACROS // #define COUNT(x) sizeof(x)/sizeof(*x) #define TEXT_HOR_ALIGN_CENTER(t) ((u8g2.getDisplayWidth() - (u8g2.getUTF8Width(t))) / 2) #define TEXT_HOR_ALIGN_RIGHT(t) (u8g2.getDisplayWidth() - u8g2.getUTF8Width(t)) //#define TEXT_HOR_ALIGN_LEFT 0 //#define ICON_HOR_ALIGN_CENTER(z) ((u8g2.getDisplayWidth() - z) / 2 ) //#define TEXT_VER_ALIGN_CENTER ((u8g2.getDisplayHeight() + 4) / 2) //#define TEXT_VER_ALIGN_DOWN(t) (u8g2.getDisplayHeight() - u8g2.getUTF8Height(t)) //#define TEXT_VER_ALIGN_UP 0 //#define ICON_VER_ALIGN_CENTER(z) ((u8g2.getDisplayHeight() - z) / 2 ) //GROVE BEGINNER KIT V2 PINOUT // const byte pDHT = 3; const byte pLED = 4; const byte pBUZZER = 5; const byte pBTN = 6; const byte pPOT = A0; const byte pSOUND = A2; const byte pLIGHT = A6; //MELODY TONES // const int REST = 0, NOTE_B0 = 31, NOTE_C1 = 33, NOTE_CS1 = 35, NOTE_D1 = 37, NOTE_DS1 = 39, NOTE_E1 = 41, NOTE_F1 = 44, NOTE_FS1 = 46, NOTE_G1 = 49, NOTE_GS1 = 52, NOTE_A1 = 55, NOTE_AS1 = 58, NOTE_B1 = 62, NOTE_C2 = 65, NOTE_CS2 = 69, NOTE_D2 = 73, NOTE_DS2 = 78, NOTE_E2 = 82, NOTE_F2 = 87, NOTE_FS2 = 93, NOTE_G2 = 98, NOTE_GS2 = 104, NOTE_A2 = 110, NOTE_AS2 = 117, NOTE_B2 = 123, NOTE_C3 = 131, NOTE_CS3 = 139, NOTE_D3 = 147, NOTE_DS3 = 156, NOTE_E3 = 165, NOTE_F3 = 175, NOTE_FS3 = 185, NOTE_G3 = 196, NOTE_GS3 = 208, NOTE_A3 = 220, NOTE_AS3 = 233, NOTE_B3 = 247, NOTE_C4 = 262, NOTE_CS4 = 277, NOTE_D4 = 294, NOTE_DS4 = 311, NOTE_E4 = 330, NOTE_F4 = 349, NOTE_FS4 = 370, NOTE_G4 = 392, NOTE_GS4 = 415, NOTE_A4 = 440, NOTE_AS4 = 466, NOTE_B4 = 494, NOTE_C5 = 523, NOTE_CS5 = 554, NOTE_D5 = 587, NOTE_DS5 = 622, NOTE_E5 = 659, NOTE_F5 = 698, NOTE_FS5 = 740, NOTE_G5 = 784, NOTE_GS5 = 831, NOTE_A5 = 880, NOTE_AS5 = 932, NOTE_B5 = 988, NOTE_C6 = 1047, NOTE_CS6 = 1109, NOTE_D6 = 1175, NOTE_DS6 = 1245, NOTE_E6 = 1319, NOTE_F6 = 1397, NOTE_FS6 = 1480, NOTE_G6 = 1568, NOTE_GS6 = 1661, NOTE_A6 = 1760, NOTE_AS6 = 1865, NOTE_B6 = 1976, NOTE_C7 = 2093, NOTE_CS7 = 2217, NOTE_D7 = 2349, NOTE_DS7 = 2489, NOTE_E7 = 2637, NOTE_F7 = 2794, NOTE_FS7 = 2960, NOTE_G7 = 3136, NOTE_GS7 = 3322, NOTE_A7 = 3520, NOTE_AS7 = 3729, NOTE_B7 = 3951, NOTE_C8 = 4186, NOTE_CS8 = 4435, NOTE_D8 = 4699, NOTE_DS8 = 4978; //DECLARE DEMO FUNCTIONS // void demoLed(byte menuSelec); void demoBuzzer(byte menuSelec); void demoOled(byte menuSelec); void demoButton(byte menuSelec); void demoPotentiometer(byte menuSelec); void demoLight(byte menuSelec); void demoSound(byte menuSelec); void demoTempHumidify(byte menuSelec); void demoAirPress(byte menuSelec); void demoAxis(byte menuSelec); //TEXTS, FUNCTIONS, AND ICONS PER EACH OPTION OF THE MENU // const char *txtMENU[] = { "LED", "BUZZER", "OLED", "BUTTON", "POTENTIOMETER", "LIGHT", "SOUND", "TEMP. & HUMIDITY", "AIR PRESSURE", "3-AXIS ACCELEMETER" }; const char *dscMENU[] = { "BLINKING LED", "PLAYING GAME MELODIES", "SHOWING A 3D CUBE EFFECT", "COUNTING BUTTON PRESSES", "MODIFYING CONTRAST LEVEL", "", "", "", "", "MOVING A BALL BY YOURSELF" }; const byte itemsMENU = COUNT(txtMENU); typedef void (*func_type)(byte menuSelec); static func_type funcDEMO[itemsMENU] = { &demoLed, &demoBuzzer, &demoOled, &demoButton, &demoPotentiometer, &demoLight, &demoSound, &demoTempHumidify, &demoAirPress, &demoAxis }; const unsigned char xbmMENU[itemsMENU][32] U8X8_PROGMEM = //16x16px { { 0x00, 0x00, 0xe0, 0x01, 0xe0, 0x01, 0x60, 0x3f, 0x7f, 0x7f, 0x7f, 0xc1, 0x60, 0x81, 0x60, 0x8f, 0x60, 0x8f, 0x60, 0x81, 0x7f, 0xc1, 0x7f, 0x7f, 0x60, 0x3f, 0xe0, 0x01, 0xe0, 0x01, 0x00, 0x00 }, { 0x00, 0x00, 0x80, 0x01, 0xc0, 0x01, 0xe0, 0x01, 0xb0, 0x11, 0x9e, 0x21, 0x8e, 0x45, 0x86, 0x49, 0x86, 0x49, 0x8e, 0x45, 0x9e, 0x21, 0xb0, 0x11, 0xe0, 0x01, 0xc0, 0x01, 0x80, 0x01, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0xfe, 0x7f, 0xfe, 0x7f, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00 }, { 0x00, 0x00, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f, 0xfe, 0x7f, 0x06, 0x60, 0x06, 0x60, 0x36, 0x6c, 0xfe, 0x7f, 0xfe, 0x7f, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x00 }, { 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfe, 0x7f, 0xff, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x9b, 0xd9, 0xff, 0xff, 0xfe, 0x7f, 0x98, 0x19, 0x98, 0x19, 0x98, 0x19 }, { 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1a, 0x58, 0x31, 0x8c, 0xe4, 0x27, 0xc2, 0x43, 0x10, 0x08, 0x08, 0x10 }, { 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0xec, 0x37, 0xcc, 0x33, 0x1c, 0x38, 0xf8, 0x1f, 0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, 0xe0, 0x07, 0xf0, 0x0f }, { 0xe0, 0x00, 0xf0, 0x01, 0x18, 0x03, 0x58, 0x7b, 0x58, 0x03, 0x58, 0x03, 0x58, 0x7b, 0x58, 0x03, 0x58, 0x03, 0x4c, 0x66, 0xe6, 0x0c, 0xf6, 0x0d, 0xe6, 0x6c, 0x0c, 0x06, 0xf8, 0x03, 0xf0, 0x01 }, { 0x00, 0x00, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0c, 0xfc, 0x0f, 0xfc, 0x07, 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x3f, 0x00, 0x30, 0xfc, 0x34, 0xfc, 0x1d, 0x80, 0x01, 0x90, 0x01, 0xe0, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xfc, 0x3f, 0xfe, 0x7f, 0x06, 0x60, 0xf6, 0x6f, 0xf6, 0x6f, 0x06, 0x67, 0x86, 0x63, 0xc6, 0x61, 0xe6, 0x60, 0xf6, 0x6f, 0xf6, 0x6f, 0x06, 0x60, 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x00 } }; //TEXTS AND ICONS PER EACH OPTION OF THE TONE MENU // const char *txtTONES[] = { "TETRIS", "MARIO", "ZELDA" }; const byte itemsTONES = COUNT(txtTONES); const unsigned char xbmTONES[itemsTONES][100] U8X8_PROGMEM = //25x25px { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x7f, 0x00, 0x8c, 0x01, 0x7f, 0x00, 0x8c, 0x01, 0x63, 0x00, 0x8c, 0x01, 0x63, 0xe0, 0xff, 0x01, 0x63, 0xe0, 0xff, 0x01, 0x7f, 0x60, 0x8c, 0x01, 0x7f, 0x60, 0x8c, 0x01, 0x63, 0x60, 0x8c, 0x01, 0x63, 0xe0, 0xff, 0x01, 0x63, 0xe0, 0xff, 0x01, 0x7f, 0x00, 0x8c, 0x01, 0x7f, 0x00, 0x8c, 0x01, 0x63, 0x00, 0x8c, 0x01, 0x63, 0x00, 0xfc, 0x01, 0x63, 0x00, 0xfc, 0x01, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0x63, 0x8c, 0x01, 0x00, 0x63, 0x8c, 0x01, 0x00, 0x63, 0x8c, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00, 0xff, 0xff, 0x01, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0xc0, 0x83, 0x07, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0x70, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x38, 0x00, 0x18, 0x42, 0x30, 0x00, 0x0c, 0x66, 0x60, 0x00, 0x0e, 0xe7, 0xe0, 0x00, 0x06, 0xff, 0xc1, 0x00, 0x86, 0xff, 0xc3, 0x00, 0x86, 0xbb, 0xc3, 0x00, 0xc6, 0x93, 0xc7, 0x00, 0xe6, 0x83, 0xcf, 0x00, 0xce, 0x01, 0xe7, 0x00, 0x8c, 0x01, 0x63, 0x00, 0x18, 0x01, 0x71, 0x00, 0x38, 0x00, 0x38, 0x00, 0x70, 0x00, 0x1c, 0x00, 0xe0, 0x00, 0x0e, 0x00, 0xc0, 0x83, 0x07, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x80, 0xff, 0x07, 0x00, 0xc0, 0xff, 0x07, 0x00, 0xc0, 0x03, 0x0f, 0x00, 0xe0, 0x86, 0x1d, 0x00, 0x60, 0xce, 0x19, 0x00, 0x30, 0xcc, 0x30, 0x00, 0x30, 0x78, 0x30, 0x00, 0x18, 0x78, 0x60, 0x00, 0xf8, 0xff, 0x7f, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; //OTHER XBMP IMAGES // const unsigned char xMenuUp[] U8X8_PROGMEM = { /*16x16px*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char xMenuDown[] U8X8_PROGMEM = { /*16x16px*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x38, 0x0e, 0x1c, 0x1c, 0x0e, 0x38, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char xMenuLeft[] U8X8_PROGMEM = { /*10x10px*/ 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00 }; const unsigned char xMenuRight[] U8X8_PROGMEM = { /*10x10px*/ 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00 }; //OBJECTS // #ifdef DEBUG HardwareSerial &debug = Serial; #endif U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R2, U8X8_PIN_NONE); DHT dht(pDHT, DHT11); BMP280 bmp280; LIS3DHTR <TwoWire> accelemeter; //ENUMS // enum Button{ ShortPress, LongPress, Unknown}; /** * */ void setup() { pinMode(pLED, OUTPUT); pinMode(pBUZZER, OUTPUT); pinMode(pBTN, INPUT); pinMode(pPOT, INPUT); pinMode(pSOUND, INPUT); pinMode(pLIGHT, INPUT); digitalWrite(pBUZZER, LOW); #ifdef DEBUG debug.begin(9600); while( !debug ); #endif if( !bmp280.init() ) { #ifdef DEBUG debug.println(F("BMP280 init error!")); #endif } accelemeter.begin(Wire, LIS3DHTR_ADDRESS_UPDATED); delay(100); accelemeter.setOutputDataRate(LIS3DHTR_DATARATE_50HZ); if( !accelemeter ) { #ifdef DEBUG debug.println(F("LIS3DHTR didn't connect!")); #endif } u8g2.begin(); } /** * */ void loop() { static byte menuSelec = 0; static byte prevMenuSelec = menuSelec; Button btnPressed; //DRAWING THE DEMO SCREEN SELECTED AND STAY THERE UNTIL A LONG PRESS BUTTON: // funcDEMO[menuSelec](menuSelec); //DRAWING THE MENU SCREEN AND STAY THERE UNTIL A SHORT PRESS BUTTON: // do{ menuSelec = readPot(0, 1023, 0, itemsMENU-1); btnPressed = readButton(); if( menuSelec != prevMenuSelec ) { prevMenuSelec = menuSelec; beep('S'); } printMenu(menuSelec); } while( btnPressed != Button::ShortPress ); } /** * */ void printMenu( byte menuSelec ) { u8g2.firstPage(); do{ //ROW 1: if( menuSelec > 0 ) { u8g2.drawXBMP(4, 2, 14, 14, xMenuUp); u8g2.setFont(u8g2_font_haxrcorp4089_tr); u8g2.setCursor(22, 13); u8g2.print(txtMENU[menuSelec-1]); } //ROW 2: u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.drawBox(0, 18, 128, 27); u8g2.setColorIndex(0); u8g2.drawXBMP(3, 24, 16, 16, xbmMENU[menuSelec]); if( strlen(dscMENU[menuSelec]) == 0 ){ u8g2.drawStr(22, 35, txtMENU[menuSelec]); }else{ u8g2.drawStr(22, 30, txtMENU[menuSelec]); u8g2.setFont(u8g2_font_micro_mr); u8g2.drawStr(22, 40, dscMENU[menuSelec]); } u8g2.setColorIndex(1); //ROW 3: if( menuSelec < itemsMENU-1 ) { u8g2.drawXBMP(4, 48, 14, 14, xMenuDown); u8g2.setFont(u8g2_font_haxrcorp4089_tr); u8g2.setCursor(22, 58); u8g2.print(txtMENU[menuSelec+1]); } drawFrame(); } while( u8g2.nextPage() ); } /** * CHANGING THE BLINKING SPEED OF THE RED LED * The code is doing a lot of things, so it doesn't have time to blink at less than 85ms */ void demoLed( byte menuSelec ) { unsigned long time = 0; static unsigned long prevTime = time; int interval; int blinking; Button btnPressed; do{ time = millis(); interval = readPot(0, 1023, 0, 100); blinking = readPot(0, 1023, 0, 1000); btnPressed = readButton(); if( time - prevTime >= blinking ) { prevTime = time; digitalWrite(pLED, !digitalRead(pLED)); } u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.drawFrame(13, 30, 102, 12); u8g2.drawFrame(14, 31, 100, 10); for( int x=0 ; x<interval ; x++ ){ u8g2.drawVLine(15+x, 32, 8); } u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 51); u8g2.print(F("MILLISECONDS:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(blinking).c_str())-14, 51 ); u8g2.print(blinking); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); digitalWrite(pLED, LOW); } /** * PLAYING A LIST OF TONES * They have had to be shorted because ATmega328p doesn't have memory enough. */ void demoBuzzer( byte menuSelec ) { int toneSelec = 0; static int prevToneSelec = toneSelec; Button btnPressed; do{ toneSelec = readPot(0, 1023, 0, itemsTONES-1); btnPressed = readButton(); if( toneSelec != prevToneSelec ) { prevToneSelec = toneSelec; beep('S'); } if( btnPressed == Button::ShortPress ) { playTone(toneSelec); } u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.drawXBMP(51, 23, 25, 25, xbmTONES[toneSelec]); if( toneSelec > 0 ) { u8g2.drawHLine(1, 50, 11); u8g2.drawHLine(1, 51, 11); u8g2.drawHLine(1, 52, 11); u8g2.drawHLine(1, 53, 11); u8g2.drawHLine(1, 54, 11); u8g2.drawHLine(1, 55, 11); u8g2.drawHLine(1, 56, 11); u8g2.drawHLine(1, 57, 11); u8g2.drawHLine(1, 58, 11); u8g2.drawHLine(1, 59, 11); u8g2.drawHLine(1, 60, 11); u8g2.drawHLine(2, 61, 10); u8g2.drawHLine(3, 62, 9); u8g2.setColorIndex(0); u8g2.drawXBMP(2, 52, 10, 10, xMenuLeft); u8g2.setColorIndex(1); } if( toneSelec < itemsTONES-1 ) { u8g2.drawHLine(116, 50, 11); u8g2.drawHLine(116, 51, 11); u8g2.drawHLine(116, 52, 11); u8g2.drawHLine(116, 53, 11); u8g2.drawHLine(116, 54, 11); u8g2.drawHLine(116, 55, 11); u8g2.drawHLine(116, 56, 11); u8g2.drawHLine(116, 57, 11); u8g2.drawHLine(116, 58, 11); u8g2.drawHLine(116, 59, 11); u8g2.drawHLine(116, 60, 11); u8g2.drawHLine(116, 61, 10); u8g2.drawHLine(116, 62, 9); u8g2.setColorIndex(0); u8g2.drawXBMP(116, 52, 10, 10, xMenuRight); u8g2.setColorIndex(1); } u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor( TEXT_HOR_ALIGN_CENTER(txtTONES[toneSelec]), 60 ); u8g2.print(txtTONES[toneSelec]); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * SHOWING A 3D CUBE EFFECT. * ATmega328p doesn't have memory enough, so using this board you can't see this demo. */ void demoOled( byte menuSelec ) { #if defined(__AVR_ATmega328P__) Button btnPressed; do{ btnPressed = readButton(); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(10, 38); u8g2.print(F("INSUFFICIENT MEMORY")); u8g2.setCursor(10, 48); u8g2.print(F("TO LOAD THE 3D DEMO")); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); #else static float rot, rotx, roty, rotz, rotxx, rotyy, rotzz, rotxxx, rotyyy, rotzzz; static int wireframe[24][2]; static float vector; const int originx = 64; const int originy = 32; Button btnPressed; do{ //cube vertices - {x, y, z} int cube_vertex[8][3] = { {-20, -20, 20}, { 20, -20, 20}, { 20, 20, 20}, {-20, 20, 20}, {-20, -20, -20}, { 20, -20, -20}, { 20, 20, -20}, {-20, 20, -20} }; for( int angle=0 ; angle <= 360 ; angle = angle + 3 ) { btnPressed = readButton(); if( btnPressed == Button::LongPress ){ break; } u8g2.firstPage(); do{ for( int i=0 ; i < 8 ; i++ ) { rot = angle * 0.0174532; //0.0174532 = one degree //rotateY rotz = cube_vertex[i][2] * cos(rot) - cube_vertex[i][0] * sin(rot); rotx = cube_vertex[i][2] * sin(rot) + cube_vertex[i][0] * cos(rot); roty = cube_vertex[i][1]; //rotateX rotyy = roty * cos(rot) - rotz * sin(rot); rotzz = roty * sin(rot) + rotz * cos(rot); rotxx = rotx; //rotateZ rotxxx = rotxx * cos(rot) - rotyy * sin(rot); rotyyy = rotxx * sin(rot) + rotyy * cos(rot); rotzzz = rotzz; //orthographic projection rotxxx = rotxxx + originx; rotyyy = rotyyy + originy; //store new vertices values for wireframe drawing wireframe[i][0] = rotxxx; wireframe[i][1] = rotyyy; wireframe[i][2] = rotzzz; } //Draw wireframe: //Face A vector = { (wireframe[0][0] * wireframe[1][1] - wireframe[0][1] * wireframe[1][0]) + (wireframe[1][0] * wireframe[2][1] - wireframe[1][1] * wireframe[2][0]) + (wireframe[2][0] * wireframe[3][1] - wireframe[2][1] * wireframe[3][0]) + (wireframe[3][0] * wireframe[0][1] - wireframe[3][1] * wireframe[0][0]) }; if( vector >= 0 ) { u8g2.drawLine(wireframe[0][0], wireframe[0][1], wireframe[1][0], wireframe[1][1]); u8g2.drawLine(wireframe[1][0], wireframe[1][1], wireframe[2][0], wireframe[2][1]); u8g2.drawLine(wireframe[2][0], wireframe[2][1], wireframe[3][0], wireframe[3][1]); u8g2.drawLine(wireframe[3][0], wireframe[3][1], wireframe[0][0], wireframe[0][1]); } //Face B vector = { (wireframe[0][0] * wireframe[4][1] - wireframe[0][1] * wireframe[4][0]) + (wireframe[4][0] * wireframe[5][1] - wireframe[4][1] * wireframe[5][0]) + (wireframe[5][0] * wireframe[1][1] - wireframe[5][1] * wireframe[1][0]) + (wireframe[1][0] * wireframe[0][1] - wireframe[1][1] * wireframe[0][0]) }; if( vector >= 0 ) { u8g2.drawLine(wireframe[0][0], wireframe[0][1], wireframe[4][0], wireframe[4][1]); u8g2.drawLine(wireframe[4][0], wireframe[4][1], wireframe[5][0], wireframe[5][1]); u8g2.drawLine(wireframe[5][0], wireframe[5][1], wireframe[1][0], wireframe[1][1]); u8g2.drawLine(wireframe[1][0], wireframe[1][1], wireframe[0][0], wireframe[0][1]); } //Face C vector = { (wireframe[4][0] * wireframe[5][1] - wireframe[4][1] * wireframe[5][0]) + (wireframe[5][0] * wireframe[6][1] - wireframe[5][1] * wireframe[6][0]) + (wireframe[6][0] * wireframe[7][1] - wireframe[6][1] * wireframe[7][0]) + (wireframe[7][0] * wireframe[4][1] - wireframe[7][1] * wireframe[4][0]) }; if( vector <= 0 ) { u8g2.drawLine(wireframe[4][0], wireframe[4][1], wireframe[5][0], wireframe[5][1]); u8g2.drawLine(wireframe[5][0], wireframe[5][1], wireframe[6][0], wireframe[6][1]); u8g2.drawLine(wireframe[6][0], wireframe[6][1], wireframe[7][0], wireframe[7][1]); u8g2.drawLine(wireframe[7][0], wireframe[7][1], wireframe[4][0], wireframe[4][1]); } //Face D vector = { (wireframe[3][0] * wireframe[7][1] - wireframe[3][1] * wireframe[7][0]) + (wireframe[7][0] * wireframe[6][1] - wireframe[7][1] * wireframe[6][0]) + (wireframe[6][0] * wireframe[2][1] - wireframe[6][1] * wireframe[2][0]) + (wireframe[2][0] * wireframe[3][1] - wireframe[2][1] * wireframe[3][0]) }; if( vector <= 0 ) { u8g2.drawLine(wireframe[3][0], wireframe[3][1], wireframe[7][0], wireframe[7][1]); u8g2.drawLine(wireframe[7][0], wireframe[7][1], wireframe[6][0], wireframe[6][1]); u8g2.drawLine(wireframe[6][0], wireframe[6][1], wireframe[2][0], wireframe[2][1]); u8g2.drawLine(wireframe[2][0], wireframe[2][1], wireframe[3][0], wireframe[3][1]); } //Face E vector = { (wireframe[0][0] * wireframe[4][1] - wireframe[0][1] * wireframe[4][0]) + (wireframe[4][0] * wireframe[7][1] - wireframe[4][1] * wireframe[7][0]) + (wireframe[7][0] * wireframe[3][1] - wireframe[7][1] * wireframe[3][0]) + (wireframe[3][0] * wireframe[0][1] - wireframe[3][1] * wireframe[0][0]) }; if( vector <= 0 ) { u8g2.drawLine(wireframe[0][0], wireframe[0][1], wireframe[4][0], wireframe[4][1]); u8g2.drawLine(wireframe[4][0], wireframe[4][1], wireframe[7][0], wireframe[7][1]); u8g2.drawLine(wireframe[7][0], wireframe[7][1], wireframe[3][0], wireframe[3][1]); u8g2.drawLine(wireframe[3][0], wireframe[3][1], wireframe[0][0], wireframe[0][1]); } //Face F vector = { (wireframe[1][0] * wireframe[5][1] - wireframe[1][1] * wireframe[5][0]) + (wireframe[6][0] * wireframe[6][1] - wireframe[5][1] * wireframe[6][0]) + (wireframe[6][0] * wireframe[2][1] - wireframe[6][1] * wireframe[2][0]) + (wireframe[2][0] * wireframe[1][1] - wireframe[2][1] * wireframe[1][0]) }; if( vector >= 0 ) { u8g2.drawLine(wireframe[1][0], wireframe[1][1], wireframe[5][0], wireframe[5][1]); u8g2.drawLine(wireframe[5][0], wireframe[5][1], wireframe[6][0], wireframe[6][1]); u8g2.drawLine(wireframe[6][0], wireframe[6][1], wireframe[2][0], wireframe[2][1]); u8g2.drawLine(wireframe[2][0], wireframe[2][1], wireframe[1][0], wireframe[1][1]); } } while (u8g2.nextPage()); } } while( btnPressed != Button::LongPress ); #endif } /** * COUNTING THE NUMBER OF TIMES THE BUTTON IS PRESSED * Sometimes the ATmega328p can't read the pulsation because it's doing another thing. * it can be fixed using interruptions but you must connect the button to a compatible pin, * for example to the D2 or D3 pint (not D6) */ void demoButton( byte menuSelec ) { unsigned long time = 0; static unsigned long prevTime = time; static unsigned int pulsations = 0; Button btnPressed; do{ time = millis(); btnPressed = readButton(); if( btnPressed == Button::ShortPress && time - prevTime >= 250 ) { prevTime = time; pulsations++; } u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 43); u8g2.print(F("PULSATIONS:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(pulsations).c_str())-14, 43 ); u8g2.print(pulsations); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * CHANGING THE CONTRACTS OF THE OLED SCREEN */ void demoPotentiometer( byte menuSelec ) { byte interval; byte contrast; Button btnPressed; do{ interval = readPot(0, 1023, 0, 100); contrast = readPot(0, 1023, 0, 255); btnPressed = readButton(); u8g2.setContrast(contrast); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.drawFrame(13, 30, 102, 12); u8g2.drawFrame(14, 31, 100, 10); for( int x=0 ; x<interval ; x++ ){ u8g2.drawVLine(15+x, 32, 8); } u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 51); u8g2.print(F("CONTRAST:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(interval).c_str())-14, 51 ); u8g2.print(interval); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * */ void demoLight( byte menuSelec ) { unsigned int value; byte disc; Button btnPressed; do{ value = analogRead(pLIGHT); disc = mapPlus1(value, 0, 748, 0, 4); btnPressed = readButton(); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.drawCircle(105, 39, 8, U8G2_DRAW_ALL); switch( disc ) { case 1: u8g2.drawDisc(105, 39, 8, U8G2_DRAW_UPPER_LEFT); break; case 2: u8g2.drawDisc(105, 39, 8, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT); break; case 3: u8g2.drawDisc(105, 39, 8, U8G2_DRAW_LOWER_LEFT | U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT); break; case 4: u8g2.drawDisc(105, 39, 8, U8G2_DRAW_ALL); break; } u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 38); u8g2.print(F("LIGHT LEVEL:")); u8g2.setCursor(14, 48); u8g2.print(value); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * */ void demoSound( byte menuSelec ) { int value; const byte lenSoundQueue = 10; int soundQueue[lenSoundQueue] = {0}; Button btnPressed; for( int i=0 ; i < lenSoundQueue ; i++ ){ soundQueue[i] = analogRead(pSOUND); } do{ value = soundFilter(analogRead(pSOUND), soundQueue, lenSoundQueue); btnPressed = readButton(); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 43); u8g2.print(F("SOUND LEVEL:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(value).c_str())-14, 43 ); u8g2.print(value); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * The DHT11 sensor has a precision of 1, so it doesn't have decimals and the code use int variable instead of float. * Change it for float if you decide to change the DHT11 for a DHT22 sensor. */ void demoTempHumidify( byte menuSelec ) { int humid; int temp; float tempMcu; Button btnPressed; do{ humid = (int)dht.readHumidity(); tempMcu = getMcuTemp(); temp = (int)dht.readTemperature(); btnPressed = readButton(); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 31); u8g2.print(F("HUMIDITY:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(humid).c_str())-22, 31 ); u8g2.print(humid); u8g2.setCursor(109, 31); u8g2.print(F("%")); u8g2.setCursor(14, 44); u8g2.print(F("TEMPERATURE:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(temp).c_str())-22, 44 ); u8g2.print(temp); u8g2.setCursor(109, 44); u8g2.print(F("C")); u8g2.setCursor(14, 57); u8g2.print(F("TEMP. MCU:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(tempMcu).c_str())-22, 57 ); u8g2.print(tempMcu); u8g2.setCursor(109, 57); u8g2.print(F("C")); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * */ void demoAirPress( byte menuSelec ) { long pressure; Button btnPressed; do{ pressure = (long)bmp280.getPressure(); btnPressed = readButton(); u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(14, 43); u8g2.print(F("PRESSURE:")); u8g2.setCursor( TEXT_HOR_ALIGN_RIGHT(String(pressure).c_str())-27, 43 ); u8g2.print(pressure); u8g2.setCursor(104, 43); u8g2.print(F("PA")); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * This module is soldering in the wrong position, * so I have needed to invert the axis to coincide the orientation with the board: * * X = This axis is the axis Y from the sensor, it has inverting positive numbers to negative and vice versa. * Y = This axis is the axis X from the sensor. * Z = Z */ void demoAxis( byte menuSelec ) { //Modify these if you want: static const byte sfx = 50; //Start Frame X static const byte sfy = 17; //Start Frame Y static const byte width = 77; static const byte height = 46; static const byte szBall = 2; //Size Ball //Don't modify these: static const byte efx = sfx + width; static const byte efy = sfy + height; static const byte sbfx = sfx + 2; static const byte sbfy = sfy + 2; static const byte ebfx = efx - 2; static const byte ebfy = efy - 2; static byte x = sfx + width / 2; static byte y = sfy + height / 2; float ax, ay, az; Button btnPressed; do{ ax = accelemeter.getAccelerationY() * -1.0; //Check commentary. ay = accelemeter.getAccelerationX(); //Check commentary. az = accelemeter.getAccelerationZ(); btnPressed = readButton(); if( x >= sbfx ) { if( ax < -0.2 && ax > -0.5 && x-1 >= sbfx ){ x -= 1; } else if( ax <= -0.5 && ax > -0.8 && x-2 >= sbfx ){ x -= 2; } else if( ax <= -0.8 && x-3 >= sbfx ){ x -= 3; } } if( x <= ebfx ) { if( ax > 0.2 && ax < 0.5 && x+1 <= ebfx ){ x += 1; } else if( ax >= 0.5 && ax < 0.8 && x+2 <= ebfx ){ x += 2; } else if( ax >= 0.8 && x+3 <= ebfx ){ x += 3; } } if( y >= sbfy ) { if( ay > 0.2 && ay < 0.5 && y-1 >= sbfy ){ y -= 1; } else if( ay >= 0.5 && ay < 0.8 && y-2 >= sbfy ){ y -= 2; } else if( ay >= 0.8 && y-3 >= sbfy ){ y -= 3; } } if( y <= ebfy ) { if( ay < -0.2 && ay > -0.5 && y+1 <= ebfy ){ y += 1; } else if( ay <= -0.5 && ay > -0.8 && y+2 <= ebfy ){ y += 2; } else if( ay <= -0.8 && y+3 <= ebfy ){ y += 3; } } u8g2.firstPage(); do{ drawFrame(); drawHeader(menuSelec); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.setCursor(4, 31); u8g2.print(F("X:")); u8g2.setCursor(20, 31); u8g2.print(ax); u8g2.setCursor(4, 44); u8g2.print(F("Y:")); u8g2.setCursor(20, 44); u8g2.print(ay); u8g2.setCursor(4, 57); u8g2.print(F("Z:")); u8g2.setCursor(20, 57); u8g2.print(az); //u8g2.drawLine(sfx, sfy, efx, sfy); //u8g2.drawLine(efx, sfy, efx, efy); //u8g2.drawLine(efx, efy, sfx, efy); u8g2.drawLine(sfx, sfy, sfx, efy); u8g2.drawDisc(x, y, szBall, U8G2_DRAW_ALL); } while( u8g2.nextPage() ); } while( btnPressed != Button::LongPress ); } /** * This function reads the button pulsations and detect when the user does a short or long pulsation. */ Button readButton() { unsigned long time = 0; static unsigned long tPrevBlink = 0; static unsigned long tBtnPressed = 0; static unsigned long tBtnReleased = 0; const unsigned long tBlinking = 50; const unsigned long tShortPress = 500; static bool bForceExit = false; Button btnPressed = Button::Unknown; if( digitalRead(pBTN) && tBtnPressed == 0 && tBtnReleased == 0 ) { tBtnPressed = millis(); digitalWrite(pLED, HIGH); } else if( digitalRead(pBTN) && tBtnPressed > 0 && tBtnReleased == 0 && !bForceExit ) { time = millis(); bForceExit = (time - tBtnPressed > tShortPress ); if( time - tPrevBlink >= tBlinking ) { tPrevBlink = time; digitalWrite(pLED, !digitalRead(pLED)); } } else if( (!digitalRead(pBTN) || bForceExit) && tBtnPressed > 0 && tBtnReleased == 0 ) { tBtnReleased = millis(); digitalWrite(pLED, LOW); } else if( tBtnPressed > 0 && tBtnReleased > 0 ) { if( tBtnReleased - tBtnPressed <= tShortPress ) { beep('S'); btnPressed = Button::ShortPress; } else { beep('L'); btnPressed = Button::LongPress; if( bForceExit ) { while( digitalRead(pBTN) ); delay(250); } } tBtnPressed = 0; tBtnReleased = 0; bForceExit = false; } return btnPressed; } /** * The potentiometer module returns a value of 1023 when is rotate on the left and 0 when is rotate on the right. * I think it's working in the wrong way, left should be 0 and right 1023, so I inverted the values. */ int readPot( int minIn, int maxIn, int minOut, int maxOut ) { return map(mapPlus1(analogRead(pPOT), minIn, maxIn, minOut, maxOut), 0, maxOut, maxOut, 0); } /** * The map() function has some problems when the difference entre the in_min and in_max is too big between out_min and out_max. * This function solves this problem. * * https://github.com/arduino/ArduinoCore-API/issues/51#issuecomment-69585614 */ long mapPlus1( long x, long in_min, long in_max, long out_min, long out_max ) { return (x - in_min) * (out_max - out_min + 1) / (in_max - in_min + 1) + out_min; } /** * Some utilities to draw the user interface and not to repeat the code. */ void drawFrame() { u8g2.drawRFrame(0, 0, 128, 64, 5); } void drawHeader( byte menuSelec ) { u8g2.drawHLine(2, 1, 124); u8g2.drawHLine(1, 2, 126); u8g2.drawHLine(0, 3, 128); u8g2.drawBox(0, 4, 128, 14); u8g2.setColorIndex(0); u8g2.drawXBMP(4, 1, 16, 16, xbmMENU[menuSelec]); u8g2.setFont(u8g2_font_nokiafc22_tu); u8g2.drawStr(22, 12, txtMENU[menuSelec]); u8g2.setColorIndex(1); } /** * */ void beep( char mode ) { tone(pBUZZER, 300); switch( mode ) { case 'S': delay(10); break; case 'L': delay(200); break; } noTone(pBUZZER); } /** * All melodies are from Robson Couto, 2019 * https://github.com/robsoncouto/arduino-songs * * 1. Tetris * 2. Super Mario Bros * 3. The legend of Zelda */ void playTone( int playTone ) { int *pMelody, tempo, notes, wholenote, divider=0, noteDuration=0; if( playTone == 0 ) { static const int melody[] = { NOTE_E5, 4, NOTE_B4, 8, NOTE_C5, 8, NOTE_D5, 4, NOTE_C5, 8, NOTE_B4, 8, NOTE_A4, 4, NOTE_A4, 8, NOTE_C5, 8, NOTE_E5, 4, NOTE_D5, 8, NOTE_C5, 8, NOTE_B4, -4, NOTE_C5, 8, NOTE_D5, 4, NOTE_E5, 4, NOTE_C5, 4, NOTE_A4, 4, NOTE_A4, 8, NOTE_A4, 4, NOTE_B4, 8, NOTE_C5, 8, NOTE_D5, -4, NOTE_F5, 8, NOTE_A5, 4, NOTE_G5, 8, NOTE_F5, 8, NOTE_E5, -4, NOTE_C5, 8, NOTE_E5, 4, NOTE_D5, 8, NOTE_C5, 8, NOTE_B4, 4, NOTE_B4, 8, NOTE_C5, 8, NOTE_D5, 4, NOTE_E5, 4, NOTE_C5, 4, NOTE_A4, 4, NOTE_A4, 4, REST, 4 }; pMelody = melody; tempo = 144; notes = sizeof(melody)/sizeof(melody[0])/2; } else if( playTone == 1 ) { static const int melody[] = { NOTE_E5, 8, NOTE_E5, 8, REST, 8, NOTE_E5, 8, REST, 8, NOTE_C5, 8, NOTE_E5, 8, NOTE_G5, 4, REST, 4, NOTE_G4, 8, REST, 4, NOTE_C5, -4, NOTE_G4, 8, REST, 4, NOTE_E4, -4, NOTE_A4, 4, NOTE_B4, 4, NOTE_AS4, 8, NOTE_A4, 4, NOTE_G4, -8, NOTE_E5, -8, NOTE_G5, -8, NOTE_A5, 4, NOTE_F5, 8, NOTE_G5, 8, REST, 8, NOTE_E5, 4, NOTE_C5, 8, NOTE_D5, 8, NOTE_B4, -4, NOTE_C5, -4, NOTE_G4, 8, REST, 4, NOTE_E4, -4, NOTE_A4, 4, NOTE_B4, 4, NOTE_AS4, 8, NOTE_A4, 4, NOTE_G4, -8, NOTE_E5, -8, NOTE_G5, -8, NOTE_A5, 4, NOTE_F5, 8, NOTE_G5, 8, REST, 8, NOTE_E5, 4, NOTE_C5, 8, NOTE_D5, 8, NOTE_B4, -4 }; pMelody = melody; tempo = 200; notes = sizeof(melody)/sizeof(melody[0])/2; } else if( playTone == 2 ) { static const int melody[] = { NOTE_AS4, 4, NOTE_F4, -4, NOTE_AS4, 8, NOTE_AS4, 16, NOTE_C5, 16, NOTE_D5, 16, NOTE_DS5, 16, NOTE_F5, 2, NOTE_F5, 8, NOTE_F5, 8, NOTE_F5, 8, NOTE_FS5, 16, NOTE_GS5, 16, NOTE_AS5, -2, NOTE_AS5, 8, NOTE_AS5, 8, NOTE_GS5, 8, NOTE_FS5, 16, NOTE_GS5, -8, NOTE_FS5, 16, NOTE_F5, 2 }; pMelody = melody; tempo = 88; notes = sizeof(melody)/sizeof(melody[0])/2; } else { #ifdef DEBUG debug.println(F("Unknown melody!")); #endif return; } wholenote = (60000 * 4) / tempo; for( int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2 ) { divider = *(pMelody + thisNote+1); if( readButton() == Button::ShortPress ){ break; } if( divider > 0 ) { noteDuration = (wholenote) / divider; } else if( divider < 0 ) { noteDuration = (wholenote) / abs(divider); noteDuration *= 1.5; } tone(pBUZZER, *(pMelody + thisNote), noteDuration*0.9); delay(noteDuration); noTone(pBUZZER); } } /** * */ int soundFilter( int NEW_DATA, int QUEUE[], char n ) { int max; int min; int sum; char i; QUEUE[0] = NEW_DATA; if( QUEUE[0] < 0 ){ QUEUE[0] = 0; } max = QUEUE[0]; min = QUEUE[0]; sum = QUEUE[0]; for( i = n - 1; i != 0; i-- ) { if( QUEUE[i] > max ){ max = QUEUE[i]; } else if( QUEUE[i] < min ){ min = QUEUE[i]; } sum = sum + QUEUE[i]; QUEUE[i] = QUEUE[i - 1]; } i = n - 2; sum = sum - max - min + i / 2; sum = sum / i; return ((int) sum); } /** * Get the temperature from the microcontroller, not all microcontroller has an internal temperature sensor. */ double getMcuTemp() { ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3)); ADCSRA |= _BV(ADEN); delay(20); ADCSRA |= _BV(ADSC); while( bit_is_set(ADCSRA,ADSC) ); return (ADCW - 324.31 ) / 1.22; } /** * http://jeelabs.org/2011/05/22/atmega-memory-use */ /* int freeRam() { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } */ |
You can see it in the following video:
Conclusion
This board is perfect to focus only in the progradation. I have been programming it for a whole week and this was so funny and addictive! (It’s truth that I didn’t have another thing to do :D). With this kit you can spend a lot of hours making a wonderful user interface and drawing all information there. You can control al screens using the button and rotatory potentiometer as well.
It’s a good way to start with Arduino, but I’m sure you will miss anything else and you will want more modules, 10 is not enough!.
Now, I will list the good, normal, and bad things that I think this kit has.
Good:
- It’s great not to need to use any cable. You only need to program the code, nothing more.
- Everything is perfectly described on the board.
- The documentation on their wiki is magnificent, they have written a lot information to start and understand each part of this kit.
- This kit has a good price for starting with Arduino, it’s less that official Arduino and include a lo of modules.
Normal:
- This boards should have had a USB-C as a connector instead of a MicroUSB.
- It’s a little difficult to push the reset button.
- The temperature and humidity sensor doesn’t have too much precision, they should have put a DHT22 instead of a DHT11.
- In my opinion there are too much analog sensors in this kit. I miss a MicroSD module to practice the reading and writing of files in a MicroSD and check it in a computer. I miss a Bluetooth, WiFi, or infrared sensor as well.
- I would like this kit include another microcontroller instead of ATmega328p, it can be full easily because the OLED screen require a lot of memory.
Bad:
- The axis sensor is soldered in the wrong position and this require you have to modify the code to fix it.
- The kit include a rotatory potentiometer instead of a rotatory encoder, it’s so hard to make a friendly user interface, if you make a main menu with submenus the selection of the submenu will affect to the main menu and you will have to move again the selection/cursor.
- The button is not connected to an interruption pin, so you have to read it every time to know if the user has pressed the button, this is inefficient and make the code difficult to program.
- We have only one button, it’s difficult to program user interface with a menu and submenus, it could have been better if they have added a rotary encoder with centre button.