Menu arduino dengan rotary encoder

Rotary encoder (shaft encoder) adalah komponen pengukuran arah putaran. Komponen ini memiliki dua pin keluaran yang menghasilkan sinyal pulsa yang diproses dahulu untuk mendapatkan arah putarannya.

Sinyal rotary encoder:

 

Rotary encoder tersedia dalam bermacam type, dan tipe yang digunakan dalam program ini ada type potensio rotary encoder.

Sinyal keluaran dari type ini terlihat dapat digambarkan sbb:

komponen aplikasi sketch menu rotary encoder berbasis arduino:

  1. Arduino Uno
  2. LCD 16×2 backpack I2C
  3. Momentary Rotary Encoder

Skema arduino menu menggunakan rotary encoder:

 

Sketch menu arduino ini menggunakan external interrupt. untuk arduino uno hanya bisa menggunakan pin 2 dan 3, sedangkan arduino mega bisa menggunakan pin 2, 3, 18, 19, 20 dan 21.

program/aplikasi arduino menu arduino dengan lcd I2c 16×2 menggunakan rotary encoder:

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
#define pinRotaryEncoderCLK     2
#define pinRotaryEncoderDT      3
#define pinRotaryEncoderSwitch  4
 
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x3f, 16, 2); // coba juga alamat 0x27 (tergantung seri back pack)
 
uint8_t maskSensorA;
uint8_t maskSensorB;
uint8_t *pinSensorA;
uint8_t *pinSensorB;
volatile bool encoderAFlag = 0;
volatile bool encoderBFlag = 0;
 
int8_t nilaiEncoder = 0;
int nilaiSetting[4];
byte setMode;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Menu rotary encoder");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  pinMode(pinRotaryEncoderCLK, INPUT_PULLUP);
  pinMode(pinRotaryEncoderDT, INPUT_PULLUP);
  pinMode(pinRotaryEncoderSwitch, INPUT_PULLUP);
 
  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();
  lcd.backlight();
 
  attachInterrupt(digitalPinToInterrupt(pinRotaryEncoderCLK), encoderARising, RISING);
  attachInterrupt(digitalPinToInterrupt(pinRotaryEncoderDT), encoderBRising, RISING);
 
  maskSensorA  = digitalPinToBitMask(pinRotaryEncoderCLK);
  pinSensorA = portInputRegister(digitalPinToPort(pinRotaryEncoderCLK));
  maskSensorB  = digitalPinToBitMask(pinRotaryEncoderDT);
  pinSensorB = portInputRegister(digitalPinToPort(pinRotaryEncoderDT));
 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("A:0     C:0");
  lcd.setCursor(0, 1);
  lcd.print("B:0     D:0");
}
 
void loop() {
  if (nilaiEncoder != 0)
  {
    Serial.println(nilaiEncoder);
    nilaiSetting[setMode] += nilaiEncoder;
    nilaiEncoder = 0;
 
    lcd.setCursor(((setMode / 2) * 8) + 2, setMode % 2);
    lcd.print(nilaiSetting[setMode]);
    lcd.print(" ");
  }
  if (!digitalRead(pinRotaryEncoderSwitch))
  {
    delay(50);
    setMode = (setMode + 1) % 4;
    while (!digitalRead(pinRotaryEncoderSwitch));
    Serial.println(setMode);
  }
}
void encoderARising() {
  if ((*pinSensorB & maskSensorB) && encoderAFlag)
  {
    nilaiEncoder = -1;
    encoderAFlag = false;
    encoderBFlag = false;
  }
  else
  {
    encoderBFlag = true;
  }
 
}
 
void encoderBRising() {
  if ((*pinSensorA & maskSensorA) && encoderBFlag)
  {
    nilaiEncoder = 1;
    encoderAFlag = false;
    encoderBFlag = false;
  }
  else
  {
    encoderAFlag = true;
  }
}

library yang digunakan:

penggunaan aplikasi sketch menu rotary encoder:

  1. tekan knob untuk memilih menu setting
  2. putar knob ke untuk mengganti nilai setting

Dokumentasi menu rotary encoder lcd i2c:

Menu dan submenu LCD TFT dengan arduino

Sistem menu bertingkat (menu dan sub menu) adalah sistem pilihan/pengaturan yang terstruktur dalam kelompok-kelompok (sub menu).

Menu arduino juga bisa diterapkan pada lcd TFT (2.4″). Keunggulan sistem menu ini adalah pengaturan item menu bisa dengan mudah dimodifikasi, karena disusun dalam ‘struct’.

 
komponen yang digunakan:

  1. Arduino Mega 2560
  2. LCD TFT 2.4″

Pengaturan menu

menu dalam program ini menggunakan struktur :

1
2
3
4
5
6
7
8
struct Menu
{
  byte tipe;
  void *variabel;
  uint16_t nilaiMin;
  uint16_t nilaiMax;
  void *subMenu;
};

struktur menu harus dibuat mengikuti struktur diatas yaitu:

  1. tipe dapat berupa UInt8, UInt16, Float, textDropDown, subMenu, dll
  2. variabel merupakan alamat dari nilai setting sesuai tipe yang diberikan
  3. nilaiMin dan nilaiMax merupakan batas setting.
  4. subMenu alamat struktur menu apabila tipenya adalah subMenu.

coding arduino menu tft lcd:

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
#define LCD_CS                A3
#define LCD_CD                A2
#define LCD_WR                A1
#define LCD_RD                A0
 
#define LCD_RESET             A4
 
#define YP                    A1
#define XM                    A2
#define YM                    7
#define XP                    6
 
#define jumlahLevelMenu       2
#define panjangTextMenu       17
#define jumlahMenuDalamSatuLayar         4
 
#define lebarKolom1           210
#define tengahKolom1          (lebarKolom1/2)
#define tinggiBaris1          197
#define lebarKolom2           106
#define tengahKolom2          (214 + (lebarKolom2/2))
#define ringXPos              0
#define ringYPos              0
#define ringRadius            100
 
#define TS_MINX 130
#define TS_MINY 141
#define TS_MAXX 920
#define TS_MAXY 935
 
#define TS_MINPRESSURE 10
#define TS_MAXPRESSURE 1000
 
//========================================================
#include <Adafruit_GFX.h>
#include <SPFD5408_Adafruit_TFTLCD.h>
#include <SPFD5408_TouchScreen.h>
 
#define BLACK   0x0000
#define WHITE   0xFFFF
#define RED     0xF800
#define GREEN   0x07E0
#define BLUE    0x001F
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define GREY    0x2108
 
#define RED2RED 0
#define GREEN2GREEN 1
#define BLUE2BLUE 2
#define BLUE2RED 3
#define GREEN2RED 4
#define RED2GREEN 5
 
enum MenuMode
{
  UInt8,
  UInt16,
  Float,
  textDropDown,
  subMenu,
  commandSettingPabrik,
};
 
struct Menu
{
  byte tipe;
  void *variabel;
  uint16_t nilaiMin;
  uint16_t nilaiMax;
  void *subMenu;
};
 
struct MenuIndex
{
  byte index;
  char *menutext;
  Menu *menu;
  byte showIndex;
  byte menuLength;
  char *dropDown;
  byte dropDownLength;
};
 
struct Setting
{
  byte lampu;
  byte alarm;
  byte kipas;
  byte kontras;
  byte kecerahan;
  byte suhuSet;
  byte kelembabanSet;
  byte rollerMode;
  long rollerJeda;
  long rollerDurasi;
  long istirahatMode;
  long istirahatkipas;
  long istirahatJeda;
  long istirahatDurasi;
  byte lampuLatar;
  byte humidifier;
  long rollerSebelumnya;
  long istirahatSebelumnya;
  byte tombol;
};
 
struct TouchScreenKode
{
  uint16_t x1;
  uint16_t y1;
  uint16_t x2;
  uint16_t y2;
  byte kode;
};
 
//variabel
bool aktif;
float suhu;
float kelembaban;
 
uint16_t intensitasCahaya;
uint16_t aliranUdara;
uint16_t levelSuara;
uint16_t warnaLampu;
 
uint8_t jam;
uint8_t menit;
uint8_t detik;
uint8_t hari;
uint8_t tanggal;
uint8_t bulan;
uint8_t tahun;
 
Setting setting;
 
//Dropdown menu
const char aktifText[][panjangTextMenu] PROGMEM =
{
  "Tidak",
  "Ya",
};
const char pilihanBatalLanjut[][panjangTextMenu] PROGMEM  =
{
  "Batal",
  "Lanjut",
};
const char pilihanHidupMati[][panjangTextMenu] PROGMEM  =
{
  "Hidup",
  "Mati",
};
const char pilihanHidupMatiAuto[][panjangTextMenu] PROGMEM  =
{
  "Hidup",
  "Mati",
  "Auto",
};
 
const char pilihanKipas[][panjangTextMenu] PROGMEM =
{
  "Hidup",
  "Mati",
  "Humidity",
  "Lampu",
};
 
const char pilihanAlarm[][panjangTextMenu] PROGMEM =
{
  "Temp",
  "roll",
  "istrh",
  "Mati"
};
 
const char menuStrWaktu[][panjangTextMenu] PROGMEM =
{
  "1. Jam",
  "2. Menit",
  "3. Detik",
  "4. Tanggal",
  "5. Bulan",
  "6. Tahun",
};
 
//Sub menu
//tipe            variabel  nilaiMin nilaiMax submenu  jumlahBaris
const Menu menuWaktu[] =
{
  {UInt8         , &jam     , 1     , 24    , 0 },
  {UInt8         , &menit   , 0     , 59    , 0 },
  {UInt8         , &detik   , 0     , 59    , 0 },
  {UInt8         , &tanggal , 1     , 31    , 0 },
  {UInt8         , &bulan   , 1     , 12    , 0 },
  {UInt8         , &tahun   , 0     , 99    , 0 },
};
 
const char menuStrPeralatan[][panjangTextMenu] PROGMEM =
{
  "1. Kipas",
  "2. Alarm",
  "3. Kontras",
  "4. Kecerahan",
  "5. Lampu Latar",
  "6. Bunyi Tmbol",
};
 
const Menu menuPeralatan[] =
{
  {textDropDown  , &setting.kipas      , 0     , (sizeof(pilihanKipas) / sizeof(pilihanKipas[0]) - 1)                , &pilihanKipas         },
  {textDropDown  , &setting.alarm      , 0     , (sizeof(pilihanAlarm) / sizeof(pilihanAlarm[0]) - 1)                  , &pilihanAlarm         },
  {UInt8         , &setting.kontras    , 0     , 99                                                                  , 0                     },
  {UInt8         , &setting.kecerahan  , 0     , 99                                                                  , 0                     },
  {textDropDown  , &setting.lampuLatar , 0     , (sizeof(pilihanHidupMatiAuto) / sizeof(pilihanHidupMatiAuto[0]) - 1)  , &pilihanHidupMatiAuto },
  {textDropDown  , &setting.tombol     , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0]) - 1)          , &pilihanHidupMati     },
};
 
const char menuStrRoller[][panjangTextMenu] PROGMEM =
{
  "1. Mode",
  "2. Jeda (jam)",
  "3. Lama(menit)",
};
 
const Menu menuRoller[] =
{
  {textDropDown  , &setting.rollerMode      , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1     , &pilihanHidupMati  },
  {UInt8         , &setting.rollerJeda      , 1     , 24                                                               , 0                  },
  {UInt8         , &setting.rollerDurasi    , 1     , 59                                                               , 0                  },
};
 
const char menuStrIstirahat[][panjangTextMenu] PROGMEM =
{
  "1. Istirahat",
  "2. Kipas",
  "3. Jeda (jam)",
  "4. Lama(menit)",
};
 
const Menu menuIstirahat[] =
{
  {textDropDown  , &setting.istirahatMode    , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1    , &pilihanHidupMati },
  {textDropDown  , &setting.istirahatkipas   , 0     , (sizeof(pilihanHidupMati) / sizeof(pilihanHidupMati[0])) - 1    , &pilihanHidupMati },
  {UInt8         , &setting.istirahatJeda    , 10    , 23                                                              , 0                 },
  {UInt8         , &setting.istirahatDurasi  , 1     , 59                                                              , 0                 },
};
 
//Menu utama
const char menuStrUtama[][panjangTextMenu] PROGMEM  =
{
  "1.Waktu",
  "2.Peralatan",
  "3.Temperatur",
  "4.Kelembaban",
  "5.Roller",
  "6.Istirahat",
  "7.Set. pabrik",
};
 
const Menu menuUtama[] =
{
  {subMenu                , &menuStrWaktu          , 0    , sizeof(menuWaktu) / sizeof(menuWaktu[0])                    , &menuWaktu          },
  {subMenu                , &menuStrPeralatan      , 0    , sizeof(menuPeralatan) / sizeof(menuPeralatan[0])            , &menuPeralatan      },
  {UInt8                  , &setting.suhuSet       , 0    , 99                                                          , 0                   },
  {UInt8                  , &setting.kelembabanSet , 0    , 99                                                          , 0                   },
  {subMenu                , &menuStrRoller         , 0    , sizeof(menuRoller) / sizeof(menuRoller[0])                  , &menuRoller         },
  {subMenu                , &menuStrIstirahat      , 0    , sizeof(menuIstirahat) / sizeof(menuIstirahat[0])            , &menuIstirahat      },
  {commandSettingPabrik   , 0                      , 0    , sizeof(pilihanBatalLanjut) / sizeof(pilihanBatalLanjut[0])  , &pilihanBatalLanjut },
};
//========================================================
 
 
 
TouchScreenKode tcKodeIdle[] = {
  {305, 730, 191, 943, 'M'},//menu utama
  {670, 716, 319, 928, 'T'},
  {867, 717, 724, 931, 'W'},
};
TouchScreenKode tcKodeMenu[] = {
  {745, 180, 692, 660, '1'},
  {634, 180, 554, 660, '2'},
  {511, 180, 442, 660, '3'},
  {390, 180, 316, 660, '4'},
  {263, 189, 176, 424, '<'},
  {263, 444, 190, 667, '>'},
  {305, 730, 191, 943, 'B'},
};
 
TouchScreenKode tcKodeEdit[] = {
  {416, 297, 327, 547, 'S'},
  {263, 189, 176, 424, '-'},
  {263, 444, 190, 667, '+'},
  {305, 730, 191, 943, 'B'},
};
 
 
MenuIndex menuIndex[jumlahLevelMenu];
 
long millismenuText;
int8_t levelMenu = -1;
bool menuEntriNilai;
char *judulMenu;
byte judulMenuTampil;
byte menu_Value8;
uint16_t menu_Value16;
float menu_ValueFloat;
 
float suhuSebelumnya;
float kelembabanSebelumnya;
 
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
 
byte lebarText;
byte ukuranText;
 
bool statusLampu;
bool statusKipas;
bool statusHumidifier;
bool rollerStatus;
bool istirahatStatus;
 
long millisRoller;
long millisIstirahat;
 
bool alarmStatus;
bool alarmPinStatus;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Menu dan submenu menggunakan LCD TFT 2.4\" berbasis arduino");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
   
  tft.reset();
  tft.begin(0x9341);
  tft.setRotation(1);
 
  idle();
 
  suhu = 32;
  kelembaban = 65;
 
  ambilDefault();
}
 
void loop()
{
  char tcKode = 0;
  if (levelMenu == -1)
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeIdle, sizeof(tcKodeIdle) / sizeof(TouchScreenKode));
  }
  else if (menuEntriNilai)
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeEdit, sizeof(tcKodeEdit) / sizeof(TouchScreenKode));
  }
  else
  {
    tcKode = ambilKodeTouchScreen((TouchScreenKode*)&tcKodeMenu, sizeof(tcKodeMenu) / sizeof(TouchScreenKode));
  }
  if (tcKode)
  {
    switch (tcKode)
    {
      case 'M':
        levelMenu++;
        menu_Display();
        break;
      case '>':
        menuIndex[levelMenu].showIndex++;
        if (menuIndex[levelMenu].showIndex > (menuIndex[levelMenu].menuLength - (jumlahMenuDalamSatuLayar / 2)))
        {
          menuIndex[levelMenu].showIndex = 0;
        }
        menu_ShowItem();
        break;
      case '<':
        if (menuIndex[levelMenu].showIndex == 0)
        {
          menuIndex[levelMenu].showIndex = (menuIndex[levelMenu].menuLength - (jumlahMenuDalamSatuLayar / 2));
        }
        else
        {
          menuIndex[levelMenu].showIndex--;
        }
        menu_ShowItem();
        break;
      case '1':
      case '2':
      case '3':
      case '4':
        menuIndex[levelMenu].index = menuIndex[levelMenu].showIndex + (tcKode - '1');
        levelMenu++;
        menu_Display();
        break;
      case 'B':
        menuEntriNilai = false;
        levelMenu--;
        menu_Display();
        break;
      case 'S':
        menu_EditSelesai();
        break;
      case '-':
        menu_KurangNilai();
        break;
      case '+':
        menu_TambahNilai();
        break;
 
    }
  }
}
 
void menu_Display()
{
  if (levelMenu == -1)
  {
    idle();
    menuIndex[levelMenu].dropDownLength = 0;
  }
  else if (levelMenu == 0)
  {
    menuIndex[levelMenu].index = 0;
    menuIndex[levelMenu].menutext = (char*)menuStrUtama;
    menuIndex[levelMenu].menu = menuUtama;
    menuIndex[levelMenu].showIndex = 0;
    menuIndex[levelMenu].menuLength = sizeof(menuUtama) / sizeof(menuUtama[0]);
    menuIndex[levelMenu].dropDownLength = 0;
 
    tft.fillRect(0, tinggiBaris1 + 4, 208, 35, BLACK);
 
    ukuranText = 3;
    tft.setFont();
    tft.setTextColor (WHITE, BLACK);
 
    tft.fillRect(0, 0, lebarKolom1, tinggiBaris1, BLACK);
    tft.setTextSize (ukuranText);
    lebarText = 10 * ukuranText * 6;
    tft.setCursor(tengahKolom1 - (lebarText / 2), 0);
    tft.print("MENU UTAMA");
 
    menu_TcMenu();
    menu_ShowItem();
  }
  else
  {
    char buf[panjangTextMenu];
    Menu menuLama = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
 
    ukuranText = 3;
    tft.setFont();
    tft.setTextColor (WHITE, BLACK);
 
    tft.fillRect(0, 0, lebarKolom1, ukuranText * 8, BLACK);
    tft.setTextSize (ukuranText);
 
    copyFlashString(buf, menuIndex[levelMenu - 1].menutext + (menuIndex[levelMenu - 1].index * panjangTextMenu));
 
    lebarText = strlen(buf) * ukuranText * 6;
    tft.setCursor(tengahKolom1 - (lebarText / 2), 0);
    tft.print(buf);
 
    switch (menuLama.tipe)
    {
      case UInt8:
      case textDropDown:
        menu_Value8 = *(uint8_t*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case UInt16:
        menu_Value16 = *(uint16_t*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case Float:
        menu_Value16 = *(float*)menuLama.variabel;
        menuEntriNilai = true;
        break;
      case subMenu:
        menuIndex[levelMenu].index = 0;
        menuIndex[levelMenu].menutext = menuLama.variabel;
        menuIndex[levelMenu].menu = menuLama.subMenu;
        menuIndex[levelMenu].showIndex = 0;
        menuIndex[levelMenu].menuLength = menuLama.nilaiMax;
        menuIndex[levelMenu].dropDownLength = 0;
 
        menu_TcMenu();
        menu_ShowItem();
        break;
      case commandSettingPabrik:
        menu_Value8 = 0;
        menuEntriNilai = true;
        break;
    }
    if (menuEntriNilai)
    {
      menu_TcEdit();
      menu_ShowNilai();
    }
  }
}
void menu_tcIdle()
{
  tft.fillRect(210, 200, 110, 40, YELLOW);
  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  lebarText = 4 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("MENU");
 
}
void menu_TcMenu()
{
  tft.fillRect(0, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect((lebarKolom1 / 2) - 2, tinggiBaris1 + 4, 4, 40, BLUE);
  tft.fillRect((lebarKolom1 / 2) + 2, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect(lebarKolom1 + 4, tinggiBaris1 + 4, 106, 36, YELLOW);
 
  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  lebarText = 1 * ukuranText * 6;
  tft.setCursor(((lebarKolom1 / 2) - lebarText) / 2, 208);
  tft.print("<");
 
  tft.setCursor((((lebarKolom1 / 2) - lebarText) / 2) + (lebarKolom1 / 2), 208);
  tft.print(">");
 
 
  tft.setTextSize (ukuranText);
  lebarText = 5 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("BALIK");
}
 
void menu_TcEdit()
{
  tft.fillRect(0, tinggiBaris1 - 44, lebarKolom1, 44, BLACK);
  tft.fillRect((lebarKolom1 / 4) - 4, tinggiBaris1 - 44 - 4, (lebarKolom1 / 2) + 8, 36 + 8, BLUE);
  tft.fillRect((lebarKolom1 / 4), tinggiBaris1 - 44, (lebarKolom1 / 2), 36, YELLOW);
 
  tft.fillRect(0, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect((lebarKolom1 / 2) - 2, tinggiBaris1 + 4, 4, 40, BLUE);
  tft.fillRect((lebarKolom1 / 2) + 2, tinggiBaris1 + 4, (lebarKolom1 / 2) - 2, 36, YELLOW);
  tft.fillRect(lebarKolom1 + 4, tinggiBaris1 + 4, 106, 36, YELLOW);
 
  ukuranText = 3;
  lebarText = 3 * ukuranText * 6;
  tft.setFont();
  tft.setTextColor (BLACK, YELLOW);
  tft.setTextSize (ukuranText);
  tft.setCursor((lebarKolom1 - lebarText) / 2, tinggiBaris1 - 40);
  tft.print("Set");
 
  lebarText = 1 * ukuranText * 6;
  tft.setCursor(((lebarKolom1 / 2) - lebarText) / 2, 208);
  tft.print("-");
 
  tft.setCursor((((lebarKolom1 / 2) - lebarText) / 2) + (lebarKolom1 / 2), 208);
  tft.print("+");
 
 
  tft.setTextSize (ukuranText);
  lebarText = 5 * ukuranText * 6;
  tft.setCursor(tengahKolom2 - (lebarText / 2), 208);
  tft.print("BALIK");
}
void menu_ShowItem()
{
  char buf[panjangTextMenu];
  byte indexMenuMulai;
 
  ukuranText = 3;
  tft.fillRect(0, ukuranText * 8, lebarKolom1, tinggiBaris1 - ukuranText * 8, BLACK);
 
  ukuranText = 2;
  tft.setTextSize (ukuranText);
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);
 
  byte menuItemSize = min(jumlahMenuDalamSatuLayar, menuIndex[levelMenu].menuLength - menuIndex[levelMenu].showIndex);
 
  for ( byte i = 0; i < menuItemSize; i++)
  {
    byte showIndex = menuIndex[levelMenu].showIndex + i;
    tft.setTextSize (ukuranText);
    tft.setCursor(0, (i * ukuranText * 20) + 50);
    copyFlashString(buf, menuIndex[levelMenu].menutext + (showIndex * panjangTextMenu));
    tft.print(buf);
 
    char *alamat;
 
    switch (menuIndex[levelMenu].menu[showIndex].tipe)
    {
      case UInt8:
        itoa(*(uint8_t*)menuIndex[levelMenu].menu[showIndex].variabel, buf, 10);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case UInt16:
        itoa(*(uint16_t*)menuIndex[levelMenu].menu[showIndex].variabel, buf, 10);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case Float:
        dtostrf(*(float*)menuIndex[levelMenu].menu[showIndex].variabel, 6, 2, buf);
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case textDropDown:
        copyFlashString(buf, menuIndex[levelMenu].menu[showIndex].subMenu + (*(uint8_t*)menuIndex[levelMenu].menu[showIndex].variabel * panjangTextMenu));
        lebarText = strlen(buf) * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print(buf);
        break;
      case subMenu:
        lebarText = 1 * ukuranText * 6;
        tft.setCursor(lebarKolom1 - (lebarText + 10), (i * ukuranText * 20) + 50);
        tft.print('>');
        break;
      case commandSettingPabrik:
        break;
    }
  }
}
 
void menu_ShowNilai()
{
  char buf[panjangTextMenu];
 
  tft.fillRect(0, 50, lebarKolom1, 100, BLACK);
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
      itoa(menu_Value8, buf, 10);
      break;
    case UInt16:
      itoa(menu_Value16, buf, 10);
      break;
    case Float:
      dtostrf(menu_ValueFloat, 6, 2, buf);
      break;
    case textDropDown:
    case commandSettingPabrik:
      copyFlashString(buf, menuEdit.subMenu + (menu_Value8 * panjangTextMenu));
      break;
  }
  ukuranText = 5;
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);
  tft.setTextSize (ukuranText);
  lebarText = strlen(buf) * ukuranText * 6;
 
  tft.setCursor((lebarKolom1 - lebarText) / 2, 70);
  tft.print(buf);
}
 
void menu_TambahNilai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
    case commandSettingPabrik:
      if (menu_Value8 < menuEdit.nilaiMax)
      {
        menu_Value8++;
      }
      else
      {
        menu_Value8 = menuEdit.nilaiMin;
      }
      break;
    case UInt16:
      if (menu_Value16 < menuEdit.nilaiMax)
      {
        menu_Value16++;
      }
      else
      {
        menu_Value16 = menuEdit.nilaiMin;
      }
      break;
    case Float:
      if (menu_Value8 < menuEdit.nilaiMax)
      {
        menu_ValueFloat += 0.1;
      }
      else
      {
        menu_ValueFloat = menuEdit.nilaiMin;
      }
      break;
  }
  menu_ShowNilai();
 
}
void menu_KurangNilai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
    case commandSettingPabrik:
      if (menu_Value8 != menuEdit.nilaiMin)
      {
        menu_Value8--;
      }
      else
      {
        menu_Value8 = menuEdit.nilaiMax;
      }
      break;
    case UInt16:
      if (menu_Value16 != menuEdit.nilaiMax)
      {
        menu_Value16--;
      }
      else
      {
        menu_Value16 = menuEdit.nilaiMax;
      }
      break;
    case Float:
      if (menu_Value8 != menuEdit.nilaiMax)
      {
        menu_ValueFloat -= 0.1;
      }
      else
      {
        menu_ValueFloat = menuEdit.nilaiMax;
      }
      break;
  }
  menu_ShowNilai();
}
void menu_EditSelesai()
{
  Menu menuEdit = menuIndex[levelMenu - 1].menu[menuIndex[levelMenu - 1].index];
 
  switch (menuEdit.tipe)
  {
    case UInt8:
    case textDropDown:
      *(uint8_t*)menuEdit.variabel = menu_Value8;
      break;
    case UInt16:
      *(uint8_t*)menuEdit.variabel = menu_Value16;
      break;
    case Float:
      *(uint8_t*)menuEdit.variabel = menu_ValueFloat;
      break;
    case commandSettingPabrik:
      ambilDefault();
      break;
  }
  menuEntriNilai = false;
 
  tft.fillRect(0, 50, lebarKolom1, 100, BLACK);
  ukuranText = 3;
  tft.setFont();
  tft.setTextColor (WHITE, BLACK);
  tft.setTextSize (ukuranText);
  lebarText = strlen("Tersimpan") * ukuranText * 6;
 
  tft.setCursor((lebarKolom1 - lebarText) / 2, 70);
  tft.print("Tersimpan");
 
  delay(1000);
 
  levelMenu--;
  menu_Display();
 
}
void idle()
{
  tft.fillScreen(BLACK);
  tft.setFont();
 
  menu_tcIdle();
}
 
char ambilKodeTouchScreen(TouchScreenKode *tcKode, byte jumlahArea)
{
  TSPoint p = ts.getPoint();
  char returnValue = 0;
  if ((p.z > TS_MINPRESSURE ) && (p.z < TS_MAXPRESSURE))
  {
    for (byte i = 0; i < jumlahArea; i++)
    {
      if ((tcKode[i].x1 > p.x) && (tcKode[i].x2 < p.x) && (tcKode[i].y1 < p.y) && (tcKode[i].y2 > p.y))
      {
        byte tsCounter;
        do
        {
          p = ts.getPoint();
          if ((p.z < TS_MINPRESSURE ) || (p.z > TS_MAXPRESSURE))
          {
            tsCounter++;
          }
          else
          {
            tsCounter = 0;
          }
        }
        while (tsCounter < 20);
        returnValue = tcKode[i].kode;
        break;
      }
    }
  }
 
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
 
  return returnValue;
}
void ambilDefault()
{
  setting.lampu = 2;
  setting.alarm = 0;
  setting.kipas = 2;
  setting.kontras = 55;
  setting.kecerahan = 100;
  setting.suhuSet = 30;
  setting.kelembabanSet = 80;
  setting.rollerMode = 0;
  setting.rollerJeda = 1;
  setting.rollerDurasi = 1;
  setting.istirahatMode = 0;
  setting.istirahatkipas = 0;
  setting.istirahatJeda = 1;
  setting.istirahatDurasi = 1;
  setting.lampuLatar = 0;
  setting.tombol = 0;
  setting.humidifier = 2;
}
byte copyFlashString(char* buf, const char* alamat)
{
  char c;
  byte l = 0;
  while (c = pgm_read_byte(alamat++))
  {
    *buf++ = c;
    l++;
  }
  *buf = 0;
  return l;
 
}

 
library yang digunakan :

  1. Adafruit_GFX_Library.zip
  2. SPFD5408.zip

Konversi font dmd ke gfx (c font vertikal ke horizontal)

Metode menyimpanan data font memiliki perbedaan antara vendor library.

Font Library DMD

DMD, DMD2, DMD3 menggunakan font dengan metode vertikal seperti ditunjukkan dalam diagram berikut:

Font library GFX

Font yang dikembangkan oleh adafruit dalam GFX menggunakan metode mendatar seperti diagram berikut :

Untuk keperluan konversi font dari dmd ke gfx bisa dilakukan dengan metode invert.

Coding berikut ditulis dengan bahasa php, jadi harus menggunakan web server seperti xampp:

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
<? php
$fontHorizontal = "";
$fontVertikal = "";
$tinggi = 0;
$lebar = 0;
$charCount = 0;
if (isset($_GET['konversiKeHorizontal']))
{
  $fontVertikal = $_GET['fontVertikal'];
  $tinggi = $_GET['tinggi'];
  $lebar = $_GET['lebar'];
  $charCount = $_GET['jumlahKarakter'];
 
 
  $verticalHex = str_getcsv ($fontVertikal, ',');
  for ($i = 0; $i < sizeof($verticalHex); $i++)
  {
    $verticalByte[$i] = hexDec($verticalHex[$i]);
  }
 
  for ($cc = 0; $cc < $charCount; $cc++)
  {
    $horizontalByte = array();
    for ($i = 0; $i < ($tinggi*$lebar / 8); $i++)
    {
      array_push($horizontalByte, 0);
    }
 
    $bit = 0;
    $byte = 0;
    $indexHorizontal = 0;
    $byteMask = 1;
 
    for ($x = 0; $x < $lebar; $x++)
    {
      for ($y = 0; $y < $tinggi; $y++)
      {
        $x2 = $x;
        $y2 = $y;
        if ($y >= 8)
        {
          $x2 += ((int)($y / 8) * $lebar);
          $y2 = $y % 8;
        }
        $indexVertical = ($x2 * 8) + $y2 + ($cc*$lebar*$tinggi);
 
        $indexVerticalByte = (int)($indexVertical / 8);
        $indexVerticalBit = $indexVertical % 8;
 
 
        $indexHorizontal = ($y * $lebar ) + $x;
        $indexHorizontalByte = (int)($indexHorizontal / 8);
        $indexHorizontalBit = $indexHorizontal % 8;
 
        $verticalBit = $verticalByte[$indexVerticalByte] & pow(2, $indexVerticalBit);
        if ($verticalBit)
        {
          $horizontalByte[$indexHorizontalByte] |= pow(2, 7 - $indexHorizontalBit);
        }
      }
    }
    for ($i = 0; $i < sizeof($horizontalByte); $i++)
    {
      $fontHorizontal . = "0x";
      if ($horizontalByte[$i] < 0x10)
      {
        $fontHorizontal . = '0';
      }
 
      $fontHorizontal . = decHex($horizontalByte[$i]);
      $fontHorizontal . = ', ';
    }
    $fontHorizontal . = '&#13;&#10;';
  }
}
?>
 
<html>
  <head><title>font Invert ( vertical to horizontal) - semesin.com</title>
  </head>
  <body>
    <h4>Konversi font DMD menjadi font GFX</h4><br>
    <form action = "" method = "get">
      Font vertikal : <br><textarea name = "fontVertikal" rows = "10" cols = "100%"> <? php echo $fontVertikal ?> < / textarea > <br>
      Tinggi : <input type = "text" name = "tinggi" value = "<?php echo $tinggi ?>"><br>
      Lebar : <input type = "text" name = "lebar" value = "<?php echo $lebar; ?>"><br>
      Jumlah : <input type = "text" name = "jumlahKarakter" value = "<?php echo $charCount; ?>"><br>
      <input type = "submit" name = "konversiKeHorizontal" value = "Konversi ke Horizontal">
    </form>
    Font horizontal : <br><textarea rows = "10" cols = "100%"> <? php echo $fontHorizontal ?> < / textarea > <br>
  </body >
</html >

 

tampilan konversi font dmd ke gfx:

Konversi masehi ke Pasaran Jawa dan Hijriah dengan metode Julian Date

Julian Date

Julian date adalah tetapan bilangan penanggalan matahari yang dihitung semenjak siang tanggal 1 Januari 4713 SM.

Penanggalan Hijriah

Tanggal 1 Muharram tahun pertama sama dengan bilangan Julian 1948439.5.

Penanggalan Jawa

Sistem Pasaran Jawa memadukan metode penanggalan Hindu, Hijriah dan Julian. Keistimewaan lainnya adalah memiliki siklus pekan, siklus pasaran, siklus wuku, siklus windu.

Sketch program konversi masehi ke pasaran jawa dan Hijriah:

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
#define epochHijriah          1948439.5f //math.harvard.edu
#define tambahKurangHijriah   0
 
struct TanggalDanWaktu
{
  uint8_t detik;
  uint8_t menit;
  uint8_t jam;
  uint8_t hari;
  uint8_t tanggal;
  uint8_t bulan;
  uint8_t tahun;
};
struct Tanggal
{
  uint8_t tanggal;
  uint8_t bulan;
  uint16_t tahun;
};
struct TanggalJawa
{
  uint8_t pasaran;
  uint8_t wuku;
  uint8_t tanggal;
  uint8_t bulan;
  uint16_t tahun;
};
struct JamDanMenit
{
  uint8_t jam;
  uint8_t menit;
};
 
uint8_t jumlahHariPerBulan[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char namaBulanMasehi[][10] = {"Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober",
                            "November", "Desember"
                           };
char namaHariMasehi[][7] = {"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu",};
char namaBulanHijriah[][14] = {"Muharram", "Safar", "Rabiul awal", "Rabiul akhir", "Jumadil awal", "Jumadil akhir", "Rajab",
                             "Sya'ban", "Ramadhan", "Syawal", "Dzulkaidah", "Dzulhijjah"
                            };
char namaBulanJawa[][12] = { "Sura", "Sapar", "Mulud", "Bakda Mulud", "Jumadilawal", "Jumadilakir", "Rejeb", "Ruwah", "Pasa",
                           "Sawal", "Sela", "Besar"
                         };
char namaWukuJawa[][13] = {"Shinta", "Landhep", "Wukit", "Kurantil", "Tala", "Gumbreg", "Warigalit", "Warigagung", "Julungwangi",
                         "Sungsang", "Galungan", "Kuningan", "Langkir", "Mandasia", "Julungpujut", "Pahang", "Kuruwelut", "Mrakeh", "Tambir",
                         "Madangkungan", "Maktal", "Wuye", "Manahil", "Prangbakat", "Bala", "Wungu", "Wayang", "Kulawu", "Dhukut", "Watugunung"
                        };
char namaHariPasaran[][7] = {"Legi", "Pahing", "Pon", "Wage", "Kliwon"};
 
TanggalDanWaktu tanggalMasehi;
Tanggal tanggalHijriah;
TanggalJawa tanggalJawa;
JamDanMenit waktuMagrib;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Konversi Penanggalan Masehi ke Hijriah (metode Julian)");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
}
 
void loop() {
  waktuMagrib = {18, 12};
 
  uint32_t jumlahHari;
  double jumlahHariMatahari;
 
  tanggalMasehi.jam = 12;
  tanggalMasehi.menit = 0;
  tanggalMasehi.detik = 0;
 
  tanggalMasehi.tanggal = 8;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;
 
  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);
 
  tanggalMasehi.tanggal = 9;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;
 
  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);
 
  tanggalMasehi.tanggal = 10;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;
 
  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);
 
  tanggalMasehi.tanggal = 11;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;
 
  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);
 
  tanggalMasehi.tanggal = 12;
  tanggalMasehi.bulan = 9;
  tanggalMasehi.tahun = 18;
 
  tanggalMasehi.hari = hariDariTanggal(tanggalMasehi);
  masehiKeHijriah(tanggalMasehi, waktuMagrib, tambahKurangHijriah, tanggalHijriah, tanggalJawa);
  printKonversi(tanggalMasehi, tanggalHijriah, tanggalJawa);
 
  while (1);
}
 
double get_julian_date(Tanggal tanggal)
{
  if (tanggal.bulan <= 2)
  {
    tanggal.tahun -= 1;
    tanggal.bulan += 12;
  }
 
  double a = floor(tanggal.tahun / 100.0);
  double b = 2 - a + floor(a / 4.0);
 
  if (tanggal.tahun < 1583)
    b = 0;
  if (tanggal.tahun == 1582) {
    if (tanggal.bulan > 10)
      b = -10;
    if (tanggal.bulan == 10) {
      b = 0;
      if (tanggal.tanggal > 4)
        b = -10;
    }
  }
 
  return floor(365.25 * (tanggal.tahun + 4716)) + floor(30.6001 * (tanggal.bulan + 1)) + tanggal.tanggal + b - 1524.5;
}
 
double konversiTanggalHijriahKeJulianDate(uint16_t tahun, uint8_t bulan, uint8_t tanggal)
{
  return (epochHijriah + tanggal + ceil(29.5 * (bulan - 1)) + (354L * (tahun - 1)) + floor((3 + (11 * tahun)) / 30)) - 1;
}
 
void masehiKeHijriah(TanggalDanWaktu masehi, JamDanMenit waktuSholatMagrib, int8_t koreksiHijriah, Tanggal &hijriah, TanggalJawa &jawa)
{
  uint16_t sisaHari;
  double julianDate = get_julian_date({masehi.tanggal, masehi.bulan, masehi.tahun + 2000});
 
  uint16_t menitMagrib = waktuSholatMagrib.jam * 60  + waktuSholatMagrib.menit;
  uint16_t menitSekarang = masehi.jam * 60 + masehi.menit;
 
  if (menitSekarang >= menitMagrib)
  {
    julianDate++;//Pergantian hari hijrah pada magrib
  }
 
  julianDate = floor(julianDate) + 0.5;
 
  Tanggal tanggalHijriah;
 
  hijriah.tahun = floor(((30 * (julianDate - epochHijriah)) + 10646) / 10631);
  hijriah.bulan = min(12.0, ceil((julianDate - (29 + konversiTanggalHijriahKeJulianDate(hijriah.tahun, 1, 1))) / 29.5) + 1);
  hijriah.tanggal = (julianDate - konversiTanggalHijriahKeJulianDate(hijriah.tahun, hijriah.bulan, 1)) + 1;
 
  long julianLong = (long)julianDate;
  jawa.pasaran = ((julianLong + 1) % 5); //0 = legi
  jawa.wuku = (((julianLong + 65) % 210) / 7); //0 = Shinta
  jawa.tanggal = hijriah.tanggal;
  jawa.bulan = hijriah.bulan;
  jawa.tahun = hijriah.tahun + 512;
 
}
uint8_t hariDariTanggal(TanggalDanWaktu tanggalDanWaktu) {
  uint16_t jumlahHari = tanggalDanWaktu.tanggal;
  for (uint8_t i = 1; i < tanggalDanWaktu.bulan; ++i)
    jumlahHari += jumlahHariPerBulan[i-1];
  if (tanggalDanWaktu.bulan > 2 && tanggalDanWaktu.tahun % 4 == 0)
    ++jumlahHari;
  jumlahHari += (365 * tanggalDanWaktu.tahun) + ((tanggalDanWaktu.tahun + 3) / 4) - 1;
 
  return ((jumlahHari + 6) % 7) + 1; // 1 Januari 2000 hari sabtu = 7
}
 
void printKonversi(TanggalDanWaktu tanggalMasehi, Tanggal tanggalHijriah, TanggalJawa tanggalJawa)
{
  Serial.print("Masehi :\t");
  Serial.print(namaHariMasehi[tanggalMasehi.hari-1]);
  Serial.print(", ");
  Serial.print(tanggalMasehi.tanggal);
  Serial.print(" ");
  Serial.print(namaBulanMasehi[tanggalMasehi.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalMasehi.tahun + 2000);
 
  Serial.print("Hijriah :\t");
  Serial.print(tanggalHijriah.tanggal);
  Serial.print(" ");
  Serial.print(namaBulanHijriah[tanggalHijriah.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalHijriah.tahun);
 
  Serial.print("Pasaran Jawa :\t");
  Serial.print(tanggalJawa.tanggal);
  Serial.print(" ");
  Serial.print(namaHariPasaran[tanggalJawa.pasaran]);
  Serial.print(", ");
  Serial.print(namaWukuJawa[tanggalJawa.wuku]);
  Serial.print(", ");
  Serial.print(namaBulanJawa[tanggalJawa.bulan - 1]);
  Serial.print(" ");
  Serial.println(tanggalJawa.tahun);
  Serial.println();
}

 

Mengirim data detektor kebakaran dari arduino ke internet dengan antarmuka code igniter

Data sensor arduino

Sensor adalah instrumen atau komponen yang mampu mendeteksi perubahan kondisi objek dalam jangkauannya. Pengukuran besaran perubahan tersebut harus diubah dahulu menjadi bentuk digital sehingga dapat diproses oleh perangkat digital lainnya.

Data variabel ini bisa dimonitoring secara lokal melalui layar monitor maupun global melalui internet.

Hal yang mendukung keandalan sistem monitoring :

  1. realtime, yaitu data yang ditampilkan merupakan kondisi masa yang singkat, misalnya di perbarui setiap 1 detik.
  2. Data memiliki identitas seperti lokasi, waktu.
  3. Data yang diterima memiliki mekanisme penyaringan sehingga data yang ditampilkan terjamin.

Sensor detektor kebakaran

Terdapat beberapa Indikasi kebakaran yaitu :

  1. Api dideteksi dengan sensor flame
  2. Suhu dibaca dengan sensor suhu
  3. Asap dibaca dengan sensor asap (smoke detector)

Dalam hal pencegahan kebakaran, ketiga variabel ini terus menerus dimonitoring secara lokal.

Monitoring detektor kebakaran ini secara global juga diperlukan untuk memberikan informasi kepada pihak terkait.

Dalam contoh ini ketiga (modul) sensor ini digunakan bersama arduino dan akan dikirimkan ke mysql server.

Akses informasi sensor dengan codeIgniter

Informasi data saat ini dapat dengan mudah diakses dimanapun, namun dengan menggunakan codeIgniter diperoleh beberapa keuntungan yaitu :

  1. Pengembangan lebih mudah dengan baris program yang dapat disederhanakan.
  2. Aksesibilitas dapat dengan mudah dikontrol.
  3. Perlindungan terhadap server terutama server data lebih terjamin.

Skema Monitoring detektor kebakaran dengan codeIgniter

Komponen yang digunakan :

  1. Arduino Uno
  2. ESP8266
  3. Flame detector
  4. Sensor asap MQ7
  5. Sensor suhu dht11
  6. Relay
  7. Buzzer

Sketch / koding monitoring detektor kebakaran esp8266 – WebServer.

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
#include "WiFiEsp.h"
#include <SoftwareSerial.h>
#include "DHT.h"
  
char ssid[] = "****";        // Isi dengan nama profil Wifi
char pass[] = "********";            // password wifi
char server[] = "x.x.x.x";
 
long waktuMintaData = 1000; //minta data setiap 1000ms
 
#define pinFlame    A0
#define pinMQ       A1
#define pinDHT      A2
#define pinBuzzer   8
#define pinRelay    9
 
float setSuhu = 31.0;
 
String Respon = "";
long waktuMulai;
bool responDariServer = false;
bool prosesKirimDataKeServer = false;
  
WiFiEspClient client;
int status = WL_IDLE_STATUS;
 
SoftwareSerial wifi(2,3);
DHT dht(pinDHT, DHT11);
  
void setup()
{
  pinMode(pinFlame, INPUT_PULLUP);
  pinMode(pinMQ, INPUT_PULLUP);
  pinMode(pinDHT, INPUT_PULLUP);
  pinMode(pinBuzzer, OUTPUT);
  digitalWrite(pinRelay, HIGH);
  pinMode(pinRelay, OUTPUT);
 
  Serial.begin(115200);
  
  wifi.begin(115200);
  WiFi.init(&wifi);
  
  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }
  
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }
  
  // you're connected now, so print out the data
  Serial.println("You're connected to the network");
    
  printWifiStatus();
  dht.begin();
  waktuMulai = millis();
}
  
void loop()
{
  float suhu = dht.readTemperature();
//print status
  Serial.println();
  Serial.print("Api = ");
  Serial.println(digitalRead(pinFlame));
  Serial.print("Asap = ");
  Serial.println(digitalRead(pinMQ));
  Serial.print("suhu = ");
  Serial.println(suhu);
   
  if(!digitalRead(pinFlame) && !digitalRead(pinMQ) && (suhu < setSuhu))
  {
    digitalWrite(pinRelay, HIGH);
    digitalWrite(pinBuzzer, LOW);
     
  }
  if(digitalRead(pinFlame))
  {
    digitalWrite(pinRelay, LOW);
    digitalWrite(pinBuzzer, HIGH);
  }
  if(digitalRead(pinMQ))
  {
    digitalWrite(pinRelay, LOW);
    digitalWrite(pinBuzzer, HIGH);
  }
  if(suhu >= setSuhu)
  {
    digitalWrite(pinBuzzer, HIGH);
  }
  //kirim data
  if(waktuMintaData < millis() - waktuMulai)
  {
    waktuMulai = millis();
    prosesKirimDataKeServer = kirimDataKeServer();
  }
 
  while (client.available())
  {
    char c = client.read();
    Respon += c;
  }
 
  Serial.print("prosesKirimDataKeServer = ");
  Serial.println(prosesKirimDataKeServer);
 
  if (!client.connected() && prosesKirimDataKeServer) {
    Serial.println("Disconnecting from server...");
    client.stop();
    responDariServer = true;
    prosesKirimDataKeServer = false;
  }
 
  // penanganan data yang diterima dari server
  if(responDariServer)
  {
    responDariServer = false;
    Serial.println(Respon);
    int posisiData = Respon.indexOf("\r\n\r\n");
    String Data = Respon.substring(posisiData+4);
    Data.trim();
  
    String variabel;
    String nilai;
  
    Serial.println("Data dari server");
    posisiData = Data.indexOf('=');
    if(posisiData > 0)
    {
      variabel = Data.substring(0,posisiData);
      nilai = Data.substring(posisiData+1);
    
      //===========Penanganan respon disini
      if(variabel == "setSuhu")
      {
        setSuhu = nilai.toFloat();
      }
//      Serial.print(variabel);
//      Serial.print(" = ");
//      Serial.println(nilai);
    }
    Respon = "";
  }
}
 
bool kirimDataKeServer()
{
  Serial.println();
  Serial.println("Starting connection to server...");
  // if you get a connection, report back via serial
  if (client.connect(server, 80)) {
    Serial.println("Connected to server");
    // Make a HTTP request
  
    client.print("GET /index.php/databaseArduino/dariArduino");
    client.print("?Api=");
    client.print(digitalRead(pinFlame));
      
    client.print("&Asap=");
    client.print(digitalRead(pinMQ));
 
    client.print("&Suhu=");
    client.print(dht.readTemperature());
      
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();
    return true;
  }
  return false;
}
  
void printWifiStatus()
{
  // print the SSID of the network you're attached to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  
  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  
  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
  
  IPAddress gateway = WiFi.gatewayIP();
  Serial.print("gateway:");
  Serial.print(gateway);
  Serial.println(" ");
}

program codeIgniter untuk monitoring data sensor arduino:

databaseArduino.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class databaseArduino extends CI_Controller {
  public function __construct() {
    parent::__construct();
  }
 
  public function dariBrowser() {
    $this->load->model('Model_databaseArduino');
    $this->Model_databaseArduino->salinDataDariBrowser();
    $data['dataSensor'] = $this->Model_databaseArduino->ambilDataDariArduino();
    $data['dataParameter'] = $this->Model_databaseArduino->ambilDataDariBrowser();
 
    $this->load->view("data_sensor", $data);
  }
 
    public function dariArduino() {
    $this->load->model('Model_databaseArduino');
    $this->Model_databaseArduino->salinDataDariArduino();
    $data['dataParameter'] = $this->Model_databaseArduino->ambilDataDariBrowser();
 
    $this->load->view("data_parameter", $data);
  }
}
?>

Model_databaseArduino.php

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
<?php
class Model_databaseArduino extends CI_Model {
 
  public $title;
  public $content;
  public $date;
 
  public function ambilDataDariArduino()
  {
    $this->load->database();
    $query = $this->db->query("SELECT * FROM (
    SELECT * FROM `data_sensor` ORDER BY `nomor` DESC LIMIT 10
    ) sub
    ORDER BY `nomor` ASC");
    $this->db->close(); 
    return $query->result();
  }
 
  public function salinDataDariArduino()
  {
    date_default_timezone_set('Asia/Jakarta'); # add your city to set local time zone
    $now = date('Y-m-d H:i:s');
 
    $this->load->database();
    $this->db->set('waktu', $now);
    $this->db->set('api', $this->input->get('Api'));
    $this->db->set('asap', $this->input->get('Asap'));
    $this->db->set('suhu', $this->input->get('Suhu'));
    $this->db->insert('data_sensor');
    $this->db->close();
  }
 
  public function ambilDataDariBrowser()
  {
    $this->load->database();
    $query = $this->db->query("SELECT * FROM `data_parameter` ORDER BY `nomor` DESC LIMIT 1");
    $this->db->close(); 
    return $query->row();
  }
 
  public function salinDataDariBrowser()
  {
    date_default_timezone_set('Asia/Jakarta'); # add your city to set local time zone
    $now = date('Y-m-d H:i:s');
 
    $this->load->database();
    $this->db->set('waktu', $now);
    $this->db->set('setSuhu', $this->input->post('setSuhu'));
    $this->db->insert('data_parameter');
    $this->db->close(); 
  }
}
?>

data_sensor.php

1
2
3
4
5
6
7
8
9
<?php
  defined('BASEPATH') OR exit('No direct script access allowed');
 
  if(isset($dataParameter))
  {
    echo "setSuhu=";
    echo $dataParameter->setSuhu;
  }
?>

data_parameter.php

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
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Welcome to CodeIgniter</title>
 
  <style type="text/css">
 
  ::selection { background-color: #E13300; color: white; }
  ::-moz-selection { background-color: #E13300; color: white; }
 
  body {
    background-color: #fff;
    margin: 40px;
    font: 13px/20px normal Helvetica, Arial, sans-serif;
    color: #4F5155;
  }
 
  a {
    color: #003399;
    background-color: transparent;
    font-weight: normal;
  }
 
  h1 {
    color: #444;
    background-color: transparent;
    border-bottom: 1px solid #D0D0D0;
    font-size: 19px;
    font-weight: normal;
    margin: 0 0 14px 0;
    padding: 14px 15px 10px 15px;
  }
 
  code {
    font-family: Consolas, Monaco, Courier New, Courier, monospace;
    font-size: 12px;
    background-color: #f9f9f9;
    border: 1px solid #D0D0D0;
    color: #002166;
    display: block;
    margin: 14px 0 14px 0;
    padding: 12px 10px 12px 10px;
  }
 
  #body {
    margin: 0 15px 0 15px;
  }
 
  p.footer {
    text-align: right;
    font-size: 11px;
    border-top: 1px solid #D0D0D0;
    line-height: 32px;
    padding: 0 10px 0 10px;
    margin: 20px 0 0 0;
  }
 
  #container {
    margin: 10px;
    border: 1px solid #D0D0D0;
    box-shadow: 0 0 8px #D0D0D0;
  }
  </style>
</head>
<body>
 
 
 
<!--
<form action="/form/data_submitted" method="get">
User Name: <input type="text" name="u_name" placeholder="Please Enter User Name" class="input_box">
<br>
User email: <input type="text" name="u_email" placeholder="Please Enter Email Address" class="input_box">
<input type="submit" value="Submit" class="submit">
</form>
-->
 
<?php
 
// echo $this->input->post('setSuhu');
 
echo form_open('databaseArduino/dariBrowser');
if(isset($dataParameter->setSuhu))
{
  echo form_input('setSuhu', $dataParameter->setSuhu);
}
else
{
  echo form_input('setSuhu', '30.0');
}
echo form_submit('suhuSubmit', 'Set Temperatur');
echo form_close();
 
echo "<br>";
echo "<strong>Data pembacaan sensor</strong>";
echo "<br>";
echo "<table>";
 
  echo "<tr>";
  echo "<td width='50'>Nomor</td>";
  echo "<td width='200'>Waktu</td>";
  echo "<td width='50'>Api</td>";
  echo "<td width='50'>Asap</td>";
  echo "<td width='50'>Suhu</td>";
  echo "</tr>";
 
  foreach ($dataSensor as $row)
{
  echo "<tr>";
  echo "<td>".$row->nomor."</td>";
  echo "<td>".$row->waktu."</td>";
  echo "<td>".$row->api."</td>";
  echo "<td>".$row->asap."</td>";
  echo "<td>".$row->suhu."</td>";
  echo "</tr>";
}
echo "</table>";
?>
</body>
</html>

file server : htdocs.zip

Trik pemograman arduino

Clock 1Hz

Clock 1Hz atau penanda detik tanpa rtc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
long millisDetik;
 
void setup() {
  millisDetik = millis();
}
 
void loop() {
  if(millisDetik != millis() / 1000L)
  {
    millisDetik = millis() / 1000L;
 
    // Kode
 
  }
}

Menghemat RAM menggunakan flash string

Arduino memiliki memory ram yang kecil, salah satu langkah penghematan adalah dengan memasukkan konstanta text / string ke dalam flash seperti kode berikut:

1
Serial.println(F("https://www.project.semesin.com"));

atau jika menggunakan alamat

1
2
3
4
5
const PROGMEM char text[] = "https://www.project.semesin.com";
void setup() {
  Serial.begin(9600);
  Serial.println((const __FlashStringHelper *)text);
}

Definisi nilai output

Nilai parameter fungsi digitalWrite relay (NC/NO) dan transistor/mosfet sering kali terbalik, untuk mempermudah pekerjaan nilainya LOW-nya lebih baik didefenisikan.

1
2
3
4
5
6
7
8
9
10
11
12
13
#define pinRelay   8
#define relayLOW   HIGH //relay dengan nilai kebalikan
 
void setup() {
  pinMode(pinRelay, OUTPUT);
}
 
void loop() {
  digitalWrite(pinRelay, relayLOW);//mati
  delay(1000);
  digitalWrite(pinRelay, !relayLOW);//hidup
  delay(1000);
}

Cast Float to Byte Array

Dalam komunikasi data berbentuk float, lebih baik mengirim data berupa byte array dari pada nilai string dari float

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Serial.begin(9600);
float nilaiFloat = 0.15625;
byte *arrayByte;
 
arrayByte = (byte*)&nilaiFloat;
Serial.print(nilaiFloat);
Serial.print(" = ");
Serial.print(arrayByte[3],HEX);
Serial.print(' ');
Serial.print(arrayByte[2],HEX);
Serial.print(' ');
Serial.print(arrayByte[1],HEX);
Serial.print(' ');
Serial.println(arrayByte[0],HEX);

Aksi tombol repeat (tekan lama dan berulang)

Penggunaan tombol untuk merubah nilai settingan naik dan turun dengan fitur ‘tekan sekali-berubah sekali’ dan ‘tekan lama-berubah berulang’  (repeating button) bisa menggunakan metode berikut:

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
#define pinTombolUp         2
pinMode(pinTombolUp, INPUT_PULLUP);
if (!digitalRead(pinTombolUp))
{
  delay(50);//debounce
  bool tombolStart = true;
  while (!digitalRead(pinTombolUp))
  {
 
 
    // Aksi tombol disini
 
 
    if (tombolStart)
    {
      //tekan pertama delay 10*50 = 500ms
      for (byte d = 0; d < 10 && !digitalRead(pinTombolUp); d++)
      {
        delay(50);
      }
      tombolStart = false;
    }
    else
    {
      delay(100);//perulangan aksi dengan delay 100ms
    }
  }
}

Konstanta memory

Konstanta memory mikrokontroller pada arduino

1
2
3
4
Serial.println(FLASHEND);//ukuran flash
Serial.println(RAMEND);//ukuran ram
Serial.println(XRAMEND);//ukuran ram tambahan
Serial.println(E2END);//ukuran eeprom

Offset anggota struct

Ketika menggunakan memori EEPROM untuk menyimpan struktur data (struct), maka untuk merujuk data tertentu dapat menggunakan fungsi ‘offsetof’ (jika alamat struct di EEPROM = 0)

1
2
3
4
5
6
7
8
9
struct Setting
{
  byte tokenSetting;
  byte jumlahDatabase;
  byte password[4];
};
 
Serial.println(offsetof(Setting, tokenSetting));
Serial.println(offsetof(Setting, jumlahDatabase));

Karakter Simbol untuk LCD Matrix

LCD karakter menyimpan font di dalam memory internalnya dengan penomoran 0 hingga 255. Sebagi contoh simbol derajat ‘°’ bernilai 223 (hex 0xDF) maka untuk mencetak karakter diluar char (0x80 – 0xFF) nilai tersebut di cast dulu menjadi char.

1
lcd.print((char)223);

Set baudrate esp8266

Penggunaan SoftwareSerial menjadi alternatif untuk komunikasi serial, namun menjadi tidak stabil pada baudrate yang tinggi. Seperti pada esp8266 yang lalu lintas komunikasi serial-nya besar akan sangat berpengaruh akibat ketidakstabilan ini. Untuk itu baudrate esp8288 mau tidak mau harus diturunkan menjadi 9600, caranya adalah dengan memberikan perintah AT command kepada esp8266 seperti berikut :

AT+UART_DEF=9600,8,1,0,0

Input keyboard untuk LCD 16×2 menggunakan Arduino

Keyboard dan mouse merupakan perangkat PC (personal computer) yang berfungsi sebagai interface atau perantara antara pengguna dan pc. Perangkat ini umumnya terhubung menggunakan interface usb, akan tetapi umumnya keyboard dan mouse juga memiliki interface PS2 dan AT Bus.

PS2

PS2 atau Personal System/2 adalah salah satu protokol komunikasi antara komputer dan perangkat lain yang dikembangkan pada tahun 1987. Untuk keyboard dan mouse standar soket PS2-nya berbentuk seperti ini :

AT Bus

AT atau advanced technology bus dikembangkan pada tahun 1984. Standar interface AT Bus untuk keyboad berbentuk seperti ini:

USB Keyboard / mouse

Saat sekarang keyboad dan mouse dengan interface PS2 dan AT bus sudah tidak ditemukan yang digantikan dengan soket dengan interface USB. Namun kebanyakan keyboard dan mouse masih mendukung interface PS2 dengan kombinasi sebagai berikut :

Skema penggunaan keyboard dan mouse sebagai input

contoh sketch/program arduino menggunakan keyboard

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
#include <PS2_Semesin.h>
#include <LiquidCrystal_I2C.h>
#include <PS2Code.h>
 
#define keyboardDATAPIN   4
#define keyboardClockPIN  3
 
#define MAX_COL 16
#define MAX_ROW  2
int8_t cols = 0;
int8_t rows = 0;
 
LiquidCrystal_I2C lcd(0x3F, MAX_COL, MAX_ROW);
PS2 keyboard;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Keyboard arduino dengan tampilan I2C LCD");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin();
  lcd.backlight();
 
  keyboard.begin( keyboardDATAPIN, keyboardClockPIN );
  keyboard.setNoBreak(1);
  keyboard.setNoRepeat( 1 );
}
 
 
void loop()
{
  if ( keyboard.available() )
  {
    char keyboardData = keyboard.read();
    if(keyboardData >= ' ' && keyboardData <= '~')
    {
      lcd.print(keyboardData);
      cols++;
      if ( cols >= MAX_COL )
      {
        cols = 0;
        rows++;
        if ( rows >= MAX_ROW )
        {
          rows = 0;
        }
      }
      lcd.setCursor( cols, rows );
      Serial.print(keyboardData);
    }
    else
    {
      Serial.println();
      Serial.print("Special char = ");
      Serial.println(keyboardData,HEX);
      switch ( keyboardData )
      {
        case PS2_KEY_ENTER:
        case PS2_KEY_KP_ENTER:
          cols = 0;
          rows++;
          if ( rows >= MAX_ROW )
            rows = 0;
          break;
        case PS2_KEY_PGDN:
          rows = MAX_ROW - 1;
          break;
        case PS2_KEY_PGUP:
          rows = 0;
          break;
        case PS2_KEY_L_ARROW:
          cols--;
          if ( cols < 0 )
          {
            cols = MAX_COL - 1;
            rows--;
            if ( rows < 0 )
              rows = MAX_ROW - 1;
          }
          break;
        case PS2_KEY_R_ARROW:
          cols++;
          if ( cols >= MAX_COL )
          {
            cols = 0;
            rows++;
            if ( rows >= MAX_ROW )
              rows = 0;
          }
          break;
        case PS2_KEY_UP_ARROW:
          rows--;
          if ( rows < 0 )
            rows = 0;
          break;
        case PS2_KEY_DN_ARROW:
          rows++;
          if ( rows >= MAX_ROW )
            rows = MAX_ROW - 1;
          break;
        case PS2_KEY_BS:
          cols--;
          if ( cols < 0 )
          {
            cols = MAX_COL - 1;
            rows--;
            if ( rows < 0 )
              rows = MAX_ROW - 1;
          }
          lcd.setCursor( cols, rows );
          lcd.write( ' ' );
          break;
        case PS2_KEY_HOME:
          cols = 0;
          rows = 0;
          break;
        case PS2_KEY_END:
          cols = MAX_COL - 1;
          rows = MAX_ROW - 1;
          break;
      }
      lcd.setCursor( cols, rows );
    }
  }
}

library LiquidCrystal-I2C.zip, PS2_Semesin.zip

Pengontrolan tegangan menggunakan PWM pada arduino

Pengontrolan tegangan berfungsi untuk menjaga kestabilan tegangan keluaran ke beban sehingga beban bisa bekerja semestinya. Contohnya jika beban lampu yang intensitas cahayanya bergantung kepada tegangan, maka dengan tegangan yang stabil akan mengeluarkan cahaya yang stabil pula.

Hal yang menyebabkan ketidakstabilan tegangan diantaranya :

  1. Perubahan beban (penambahan dan pengurangan)
  2. Perubahan nilai masukan (input)
  3. Faktor luar seperti interferensi.

Faktor yang mempengaruhi keandalan pengontrolan tegangan :

  1. Kecepatan respon dari pengontrol tegangan terhadap perubahan yang terjadi, semakin cepat semakin baik.
  2. Sistem koreksi yang digunakan, seperti PID, fuzzy
  3. Karakteristik sensor dan beban yang digunakan

Pengontrolan tegangan dengan arduino

Pengendalian tegangan harus memiliki masukan sensor tegangan dan aktuator kontrol tegangan. Pada aplikasi arduino pembacaan tegangan menggunakan ADC dan aktuasi kontrol tegangan menggunakan PWM.

Dalam contoh ini, sistim koreksi tegangan menggunakan metode proporsional, yaitu semakin besar selisih tegangan dan input maka akan semakin besar pula penambahan nilai PWM.

skema sistem kontrol tegangan mengguakan arduino:

Sketch / koding Penngendalian Tegangan dengan PWM:

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
#define pinSensorTegangan     0
#define pinOutputPWM          9
#define setTegangan           2.5//volt
#define faktorProporsional    0.1
 
float keluaran;
 
void setup() {
  pinMode(pinOutputPWM, OUTPUT);
   
  Serial.begin(9600);
  Serial.println("Sumber tegangan stabil (automatic voltage regulator) menggunakan kontrol proporsional");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  keluaran = setTegangan;
}
 
void loop() {
  uint16_t adc = analogRead(pinSensorTegangan);
  float tegangan = map(adc, 0, 1024, 0, 500)/100.0;
  float selisih = setTegangan - tegangan;
  float proporsional = faktorProporsional * selisih;
   
  keluaran += proporsional;
  keluaran = constrain(keluaran, 0, 5);
  byte keluaranPWM = map(keluaran*100, 0, 5*100, 0, 255);
  analogWrite(pinOutputPWM, keluaranPWM);
 
  //Plot serial, hapus untuk menambah kecepatan
  Serial.print(tegangan);
  Serial.print(", ");
  Serial.print(keluaranPWM);
  Serial.println();
  delay(10);
}

Hasil Plot sinyal PWM (merah) dan tegangan keluaran (biru) terhadap perubahan beban.

Tombol power otomatis arduino

tombol pengunci (self-latching)

Rangkaian kunci tombol otomatis bisa diterapkan dengan bermacam komponen utama, yang umum digunakan adalah dengan menggunakan relay, transistor, mosfet dan SCR.

Penggrendel tombol dengan relay

Dalam sistem listrik rangkaian ini dikenal sebagai DOL (direct on line). Jika tombol ‘nyala’ ditekan maka tegangan akan masuk ke relay melalui tombol ‘padam’, kemudian relay akan aktif dan NO dalam keadaan kontak dan mengunci tegangan tetap masuk ke coil relai dan mengunci posisi relay aktif.

Jika tombol ‘padam’ ditekan dalam keadaan rangkaian aktif, maka suplai  kunci akan terputus dan relay kembali nonaktif.

Pengancing tombol dengan SCR

Masih menggunakan interface yang sama tetapi menggunakan SCR/thyristor 2P4M sebagai penguncinya. Jika tombol ‘nyala ditekan, akan mengalirkan arus ‘gate trigger’ melalui resistor 330 ohmm dan menjadikan SCR dalam keadaan on-state yang selalu aktif walaupun tombol ‘nyala dilepaskan’.

Jika tombol ‘padam’ ditekan, maka suplay arus ke SCR melalui beban outpun akan hilang dan otomatis akan mematikan operasi SCR.

Tombol Vcc otomatis pada arduino menggunakan MOSFET

Tombol power dan arduino bisa dikombinasikan (saling melengkapi) antara pemberi sumber, dan pengunci sumber.

Cara kerjanya adalah pada saat tombol power ditekan, gate pada mosfet akan diberi tegangan 5V, dan menjadikan mosfet menghantarkan GND arduino ke ground sumber.

Sesaat setelah menerima power dari sumber/baterai melalui mosfet, arduino akan menjalankan perintah untuk mengunci kondisi mosfet tetap aktif.

Untuk menonaktifkan mosfetm, cukup dengan menjadikan pin Gate bernilai LOW.

contoh sketch Sumber baterai dengan tombol otomais:

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
#define pinPower A0
#define pinLED   13
 
void setup() {
  Serial.begin(9600);
  Serial.println("Power arduino otomatis menggunakan MOSFET");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
  Serial.println("Jangan beri power supply / catu daya USB");
  Serial.println("Akan dimatikan dalam 10 detik");
  Serial.println();
   
  pinMode(pinPower, OUTPUT);
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinPower, HIGH);
  digitalWrite(pinLED, LOW);
 
  delay(7000);
  digitalWrite(pinLED, HIGH);//Tanda arduino akan dimatikan 3 detik lagi
  delay(3000);
 
  //Fungsi mematikan power Vcc
  digitalWrite(pinPower, LOW);
   
}
 
void loop() {
 
}

Tombol power otomatis pada arduino menggunakan SCR (hemat energi)

Prinsip kerja power otomatis pada arduino ini adalah ketika tombol ‘power’ ditekan, maka gate dari scr akan diberi trigger untuk mengaktifkan SCR, setelah tombol dilepaskan SCR akan tetap aktif tanpa sumber arus lain. Tidak seperti menggunakan transistor atau mosfet dimana arus dan tegangan harus disuplai dari arduino.

Untuk mematikan power ini, cukup dengan meng-ground-kan / logika LOW pada pin gate.

contoh sketch kunci tombol power otomatis:

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
#define pinPower A0
#define pinLED   13
 
void setup() {
  Serial.begin(9600);
  Serial.println("Power arduino otomatis ");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
  Serial.println("Jangan beri power supply / catu daya USB");
  Serial.println("Akan dimatikan dalam 10 detik");
  Serial.println();
   
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED, LOW);
 
  delay(7000);
  digitalWrite(pinLED, HIGH);//Tanda arduino akan dimatikan 3 detik lagi
  delay(3000);
 
  //Fungsi mematikan power Vcc
  digitalWrite(pinPower, LOW);
  pinMode(pinPower, OUTPUT);
   
}
 
void loop() {
 
}

Komunikasi data Bluetooth dengan App Inventor

Bluetooth adalah Sistem jaringan lokal tanpa kabel. Bluetooth telah digunakan secara luas pada perangkat android dan telepon pintar lainnya.

Menghubungkan ke bluetooth server

Untuk mempermudah jalan program app inventor, proses menghubungkan bluetooth ke server dimasukkan kedalam sebuah prosedur.

Prosedur ‘Hubungkan_Bluetooth’ ini bisa dipanggil dengan variabel ‘alamatBluetooth’ yang berisi alamat unik bluetooth. Sebelum dihubungkan prosedur akan mengecek status ‘bluetooth.available’, jika aktif, dilanjutkan dengan pengecekan status ‘bluetooth.IsConnected’, dan mematikan bluetooth jika sedang terkoneksi.

Selanjutnya bluetooth dihubungkan dengan fungsi ‘bluetoothClient.Connect’ dengan variabel alamat yang diberikan.

Pemanggilan bluetooth saat Inisialisasi

Apabila sistem menghubungkan bluetooth dibuat otomatis, maka bluetooth akan dihubungkan keserver dengan alamat yang tersimpan sebelumnya pada database ‘TinyDB’. Pemanfaatan database ini adalah untuk otomatisasi data saat aplikasi app dibuka tanpa perlu disetting ulang, cukup menggunakan settingan terakhir.

Koneksi bluetooth dengan alamat langsung

Jika alamat bluetooth server sudah diketahui, app bisa langsung mengkondisikan tombol sebagai pemanggil prosedur ‘hubungkan_bluetooth’ dengan alamat yang ada pada ‘textbox’.

Menghubungkan bluetooth dengan alamat pada daftar bluetooth

Daftar perangkat bluetooth yang terhubung ke android bisa diambil sebagai referensi alamat. Sebelum digunakan, elemen ‘listPicker’ digunakan sebagai penampung daftar bluetooth yang tersedia.

Apabila item yang ada dalam daftar sudah dpilih, maka akan dipanggil prosedur ‘hubungkan_Bluetooth’ dengan variabel alamat terpilih.

Putuskan hubungan bluetooth

Jika ingin memutuskan bluetooth dari server, cukup menggunakan fungsi ‘BluetoothClient.Disconect’

Cek koneksi bluetooth

Status konksitas bluetooth bisa diketahui dengan fungsi ‘BluetoothClient.IsConnected’

Mengirim data string melalui bluetooth

Untuk mengirim data melalui bluetooth, terlebih dahulu dipastikan koneksi bluetooth dengan fungsi’bluetoothClient.IsConnected’. Dan apabila terhubung, proses pengiriman string dilakukan dengan memanggil fungsi ‘bluetoothClient.SendText’ dengan variabel ‘text’ yang diisi contohnya text dalam ‘textBox’.

Menerima data dari bluetooth

Jika ada data yang dikirim melalui bluetooth, data tersebut bisa dideteksi dengan fungsi ‘bluetoothClient.SendText’ seperti pada block berikut :

Dengan bantuan ‘clock.Timer’, setiap interval waktu dilakukan pengecekan apabila ada data yang diterima lewat bluetooth, kemudian mengambilnya dengan fungsi ‘bluetooth.ReceiveText’.

Namun fungsi diatas memiliki beberapa kelemahan yaitu:

  1. Data yang diterima tidak diparsing, sehingga data apapun yang diterima adalah merupakan deretan String.
  2. Jika dalam satu interval waktu diterima beberapa data, maka data tersebut dianggap suatu kesatuan.
  3. Jika proses pengiriman data berlangsung saat interupsi timer terjadi, maka data tersebut akan terpotong (data tidak utuh).

Untuk menutui itu digunakan algoritma app inventor yang lebih rumit namun mampu berjalan pada jumlah data besar.

Proses pengambilan data dari buffer android dilakukan secara parsial sehingga terhindar dari pemotongan, dengan metode ini dimungkinkan menerima beberapa nilai variabel sekaligus (dalam lalu lintas kecepatan tinggi).

Dalam contoh ini digunakan format ‘namaVariabel’=’nilai’, dalam contoh sketch arduino :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setup() {
  Serial.begin(9600);
  Serial.println("Koneksi Beluetooth pada Android dengan App Inventor");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
}
 
byte variabel1 = 0;
uint16_t variabel2 = 1000L;
 
void loop() {
  while (Serial.available())
  {
    Serial.write(Serial.read());
  }
  delay(1000);
  Serial.println("var1=" + String(variabel1));
  Serial.println("var2=" + String(variabel2));
  variabel1 += 1;
  variabel2 += 10;
}

Mendeteksi kegagalan bluetooth

Fungsi ‘bluetoothClient.BluetoothError’ akan mendeteksi jika terjadi kegagalan bluetooth seperti kesalahan dalam proses menghubungkan, mengirim atau menerima data. Blok diatas akan mengirimkan pesan kepada pengguna melalui label ‘pesan’.

Design cara komunikasi bluetooth app inventor:

Block lengkap sistem komunikasi bluetooth android:

apk menghubungkan bluetooh dengan app inventor:

Multi screen dengan App Inventor

Aplikasi App Inventor dibuat diatas screen. Screen adalah halaman tempat meletakkan interface, layout dan komponen lainnya.

Tutorial pindah antar screen ini mencakup metode pindah screen yang biasa digunakan.

Layout scroll

Apabila ruang (space) satu screen tidak mencukupi, maka bisa memanfaatkan layout dalam mode scroll baik vertikal maupun horizontal.

Penggunaan layout scroll sangat efektif karena ruang yang disediakan lebih besar.

Banyak Screen App Inventor

Pada aplikasi App Inventor, kita bisa membangun sebuah app dengan lebih dari satu screen. Dengan banyak screen kita bisa mengelompokkan komponen-komponen agar lebih terorganisir.

Screen bertindak sebagai alas tempat komponen. Apabila sebuah screen saat ini (screen induk) membuka screen lainnya (screen anak), maka screen anak akan menimpa screen induk.

Perpindahan antar Screen dengan nilai awal dan nilai balik

Sebuah screen bisa mengirim nilai awal (start value) ke screen anaknya. juga sebaliknya screen anak bisa mengirim hasil (result) ke screen induknya.

Dari contoh diatas :

  1. Button1 adalah tombol untuk membuka ‘screen2’, dengan kontrol ‘open another screen’ dan variabel ‘screenName’ diisi dengan ‘Screen2’, maka ketika button1 ditekan ‘screen2’ akan dibuka diatas ‘screen1’.
  2. Button2 adalah tombol untuk membuka screen3, pemanfaatan fungsi ‘open another screen with start value’ dengan variabel ‘screenName’ dan ‘startValue’ diisi dengan input interface ‘textBox1.text’, maka screen induk akan mengirimkan nilai awal dalam ‘textbox1.text’ ke ‘screen3’.
  3. Fungsi ‘other screen closed’ akan dijalankan saat terdeteksi sebuah screen anak ditutup dan dan kembali ke screen ini (induk). variabel ‘result’ akan memiliki nilai yang dikirim oleh screen anak.

Perpindahan antar screen anak

Pindah atar screen akan bisa dalam dua metode:

  1. Pindah ke screen anak lainnya sebagai screen cucu (tanpa menutup screen sekarang)
  2. Pindah ke screen anak lainnya sebagai ganti anak (dengan menutup screen sekarang).

Pada blok diatas adalah metode pindah antar screen anak sebagai ganti screen sekarang dengan tetap menginduk ke ‘screen1’.

Penggunaan fungsi ‘if’ dengan variabel ‘true’ hanya sebuah trik untuk menempatkan prosedur/fungsi lain dibawah fungsi ‘open another screen’.

Nilai awal dari screen induk

Saat ‘screen3’ dibuka, app akan menjalankan terlebih dahulu fungsi ‘Initialize’. Fungsi ini ditugaskan mengambil nilai awal yang dikirimkan oleh screen induk dalam variabel ‘get start value’ dan menyimpannya kedalam interface ‘textBox1/text’

Mengirim nilai balik ke screen induk

Ada beberapa cara untuk kembali ke screen induk, diantaranya adalah fungsi ‘BackPressed’ atau penggunaan tombol.

Fungsi ‘close screen with value’ berfungsi menutup screen dengan mengirimkan nilai balik dalam variabel ‘result’.

design app inventor banyak screen:

Screenshoot tukar screen app inventor:

apk multi screen app inventor: MultiScreen.apk

Fungsi Waktu App Inventor

Data waktu pada App Inventor berupa tanggal dan waktu yang dinyatakan dalam orde milidetik dari 1 Januari 1970. Data waktu ini bisa digunakan untuk beberapa keperluan diantaranya hitung mundur dan durasi.

Data waktu Sekarang (now)

Interface / modul clock memiliki fungsi ‘now’ yang menyediakan data waktu serta fungsi format datetime guna mendapatkan text waktu yang diinginkan.

Block diatas akan mengganti text pada label1 dengan text waktu sekarang dalam format MM/dd/yyyy hh:mm:ss a. Penggantian dilakukan setiap interval dari clock1 (default 1 detik).

Ambil waktu sistem android

Untuk mengambil data waktu ketika tombol di tekan dengan format tertentu bisa menggunakan block berikut:

Ketika button1 ditekan, text pada label2 akan diganti dengan waktu sekarang dalam format hh:mm.

Edit data waktu dengan TimePicker

TimePicker berfungsi sebagai dialog pengedit waktu dengan nilai default waktu sekarang (now)

Ketika tombol [selesai] pada dialog timePicker ditekan, text pada tombol [TimePicker] akan diganti dengan hasil pengeditan jam dan menit.

Angka dua digit (time pattern) app inventor

Agar tampilan waktu jam:menit terlihat rapi dalam format waktu hh:mm, App inventor 2 belum mengakomodir text pattern (kecuali sensor clock), untuk itu bisa dikombinasikan dengan perhitungan matematis yakni apabila angka kecil dari 10 maka akan ditambahkan ‘0’ diawalnya.

Hitung mundur (Countdown)

Hitung mundur adalah metode penghitungan waktu tunda. Hitung mundur bisa dimanfaatkan untuk berbagai keperluan seperti aktifasi sebuah interface/screen dan sebagainya.

Hitung mundur dengan app inventor diaplikasikan dalam orde mili detik, agar lebih mudah waktu hitung mundur dikonversi menjadi detik dengan fungsi DurationToSeconds.

Untuk perancangan hitung mundur dengan app inventor dibutuhkan variabel waktu mulai dan durasi. Dengan bantuan ‘Clock’ penghitungan dideteksi setiap detik.

Hitung munder dengan metode time epoch ini bisa disetting dalam orde tahunan atau sesuai kebutuhan.

Menghitung durasi dengan app inventor

Durasi / selisih waktu adalah waktu yang dibutuhkan antara dua evenr/trigger, hasilnya diperoleh dengan formula waktu event2 dikurangi waktu event1.

Penghitungan durasi diukur dalam orde mili detik.

Block lengkap fungsi waktu menggunakan app inventor:

Design fungsi waktu dengan app inventor:

Tampilan app fungsi waktu memakai app inventor:

File apk fungsi waktu pada app inventor: FungsiWaktu.apk

Hitung mundur Arduino (volatile countdown)

Hitung mundur volatile (tidak menguap) adalah metode hitung mundur digital yang terus berlangsung walaupun catu daya (power supply) dimatikan. Jadi hitung mundur berlanjut sesuai waktu normal ketika catu daya aktif kembali.

Countdown arduino ini berfungsi sebagai penghitung mundur hingga jangka tahunan karena menggunakan metode epoch time. Waktu acuan yang digunakan adalah waktu RTC (DS1307/DS3231).

Waktu epoch adalah jumlah detik hingga saat ini dari tangga 1 Januari 1970.

Hitung mundur menggunakan Arduino dan RTC memanfaatkan EEPROM untuk menyimpan data-data berikut :

  1. Aktif
  2. Waktu mulai hitung mundur (epoch waktu)
  3. Waktu hitung mundur (dalam detik)

Karena perangkat ini memiliki fitur volatile maka dibutuhkan mekanisme pengujian/pengecekan data waktu yang disimpan yaitu :

  1. Apakah ada hitung mundur yang aktif
  2. Jika aktif apakah epoch waktu sekarang lebih besar dari epoch waktu hitung mundur mulai
  3. jika aktif apakan epoch waktu sekarang kecil dari jumlah epoch waktu mulai ditambah jumlah detik hitung mundur.

jika syarat ini di penuhi maka hitung mundur dengan arduino dilanjutkan.

berikut skema Arduino countdown (berlaku untuk DS1307/DS3231):

koding / sketch arduino hitung mundur:

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
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <EEPROM.h>
 
#define alamatEEPROMCountDownAktif  0
#define alamatEEPROMCountDownMulai  1
#define alamatEEPROMCountDownDetik  5
 
byte countDownAktif;
uint32_t countDownMulai;
uint32_t countDownDetik;
uint32_t RTCEpoch;
byte detikSebelumnya = 60;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Hitung mundur Arduino (volatile countdown)");
  Serial.println("https://www.project.semesin.com/");
  Serial.println("Entri waktu hitung mundur (dalam detik) :");
  Serial.println();
 
  countDownAktif = EEPROM.read(alamatEEPROMCountDownAktif);
  if(countDownAktif)
  {
    EEPROM.get(alamatEEPROMCountDownMulai, countDownMulai);
    EEPROM.get(alamatEEPROMCountDownDetik, countDownDetik);
    Serial.println("Hitung mundur aktif");
  }
   
}
 
void loop() {
  tmElements_t tm;
 
  if(Serial.available())
  {
    if (RTC.read(tm))
    {
      delay(200);
      countDownDetik = Serial.parseInt();
      countDownMulai = makeTime(tm);
      countDownAktif = true;
      EEPROM.write(alamatEEPROMCountDownAktif, countDownAktif);
      EEPROM.put(alamatEEPROMCountDownDetik, countDownDetik);
      EEPROM.put(alamatEEPROMCountDownMulai, countDownMulai);
      Serial.println("Waktu hitung mundur = " + String(countDownDetik) + " detik");
    }
    else
    {
      Serial.println("Gagal membaca RTC");
    }
  }
   
  if (RTC.read(tm))
  {
    if(detikSebelumnya != tm.Second)
    {
      if(countDownAktif)
      {
        RTCEpoch = makeTime(tm);
        uint32_t waktuCountDown = countDownDetik - (RTCEpoch - countDownMulai);
        Serial.println("Hitung mundur : " + String(waktuCountDown) + " detik");
   
        if(RTCEpoch < countDownMulai)
        {
          countDownAktif = false;
          EEPROM.write(alamatEEPROMCountDownAktif, countDownAktif);
          Serial.println("Hitung mundur di matikan karena Waktu RTC salah");
          Serial.println("Entri waktu hitung mundur (dalam detik) :");
        }
        else if(RTCEpoch > (countDownMulai + countDownDetik))
        {
          countDownAktif = false;
          EEPROM.write(alamatEEPROMCountDownAktif, countDownAktif);
          Serial.println("Hitung mundur kadaluarsa");
          Serial.println("Entri waktu hitung mundur (dalam detik) :");
        }
        else if(waktuCountDown == 0)
        {
          countDownAktif = false;
          EEPROM.write(alamatEEPROMCountDownAktif, countDownAktif);
          Serial.println("Hitung mundur berakhir");
          Serial.println("Entri waktu hitung mundur (dalam detik) :");
        }
      }
      detikSebelumnya = tm.Second;
    }
  }
  else
  {
    Serial.println("Gagal membaca RTC");
  }
  delay(100);
}

Keluaran serial monitor hitung mundur berbasis arduino:

Menu LCD Arduino dengan keypad

Aplikasi menu arduino memang menarik namun tidak mudah untuk dibuat. Arduino menggunakan menu merupakan aplikasi yang menampilkan sejumlah pilihan sehingga pengguna bisa memilih/merubah pilihannya.

Menu interaktif lebih cocok digunakan apabila sejumlah pilihan tidak bisa ditampilkan dalam satu halaman. Misalnya menampilkan menu pada LCD karakter 16×2 yang hanya bisa menampung 16 karakter setiap barisnya.

Salah satu menu yang sering digunakan adalah menu-menu makanan dan minuman pada penerapan restoran yang menggunakan sistem digital terkoneksi.

Kelebihan Menu I2C LCD Arduino ini adalah:

  1. Tampilan interaktif bergilir setiap 1 detik dan tampil 5 detik ketika hendak dipilih.
  2. Menggunakan keypad 4×4 sehingga lebih lega.

Video menu interaktif arduino:

Dalam perancangan berbasis arduino ini digunakan komponen berikut :

  1. Arduino Uno
  2. LCD 1602 + I2C
  3. Keypad 4×4

skema menu arduino LCD dan keypad:

koding/sketch menu keypad arduino:

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
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
 
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {4, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10, 11};
 
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
 
LiquidCrystal_I2C lcd(0x3F, 16, 2);
 
struct daftarMenu {
  char strMenu[17];
  uint32_t harga;
  bool pilihan;
};
 
char menuUtama[][17] = {
  "0..9 - Pilih    ",
  "* - Pilih       ",
  "# - Batal       ",
  "A - Makanan     ",
  "B - Minuman     ",
  "C - Total/Pesan ",
  "D - Batal       ",
};
daftarMenu menuMakanan[] = {
  {"1 Nasi Goreng   ", 13000L, false},
  {"2 Mie Goreng    ", 8000L, false},
  {"3 Bihun Goreng  ", 8000L, false},
  {"4 Mie Rebus     ", 6000L, false},
  {"5 Gado-gado     ", 13000L, false},
  {"6 Soto Padang   ", 15000L, false},
  {"7 Sate Padang   ", 18000L, false},
};
daftarMenu menuMinuman[] = {
  {"1 Es Campur     ", 6000L, false},
  {"2 Es Tebak      ", 7500L, false},
  {"3 Es Kosong     ", 2000L, false},
  {"4 Jus Jeruk     ", 6000L, false},
  {"5 Jus Pokat     ", 6500L, false},
  {"6 Kopi          ", 4000L, false},
  {"7 Teh Panas     ", 4000L, false},
  {"8 Teh Telur     ", 8000L, false},
};
 
int8_t indexMenu = -1;
byte menuLevel = 0;
byte menuLevelSebelumnya = -1;
 
enum ModeMenu {
  modeMenuMakanan,
  modeMenuMinuman,
};
ModeMenu modeMenu;
#define standarWaktuTampil  1000L
#define LihatWaktuTampil    5000L
#define jumlahMakanan       sizeof(menuMakanan)/sizeof(menuMakanan[0])
#define jumlahMinuman       sizeof(menuMinuman)/sizeof(menuMinuman[0])
 
uint16_t waktuTampil;
unsigned long millisMulai;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Menu LCD Arduino dengan keypad");
  Serial.println("https://www.project.semesin.com/");
 
  Wire.begin();
  Wire.beginTransmission(0x3F);
  if (Wire.endTransmission())
  {
    lcd = LiquidCrystal_I2C(0x27, 16, 2);
  }
  lcd.begin ();
  lcd.backlight();
  //  tampilanDepan();
  millisMulai = millis();
  resetPilihan();
}
 
void loop()
{
  char key = keypad.getKey();
 
  if (key) {
    Serial.println(key);
    switch (key)
    {
      case 'A':
        menuLevel = 1;
        indexMenu = -1;
        modeMenu = modeMenuMakanan;
        break;
      case 'B':
        menuLevel = 1;
        indexMenu = -1;
        modeMenu = modeMenuMinuman;
        break;
      case 'C':
        menuLevel = 2;
        updateMenu();
        break;
      case 'D':
        resetPilihan();
        menuLevel = 0;
        indexMenu = -1;
        updateMenu();
        break;
      case '*':
        if (menuLevel == 2)
        {
          pesananMasuk();
        }
        else
        {
          if (waktuTampil == LihatWaktuTampil)
          {
            if (modeMenu == modeMenuMakanan)
            {
              menuMakanan[indexMenu].pilihan = true;
            }
            else if (modeMenu == modeMenuMinuman)
            {
              menuMinuman[indexMenu].pilihan = true;
            }
            updateMenu();
          }
          else
          {
            waktuTampil = LihatWaktuTampil;
            millisMulai = millis();
          }
        }
        break;
      case '#':
        if (menuLevel == 2)
        {
          menuLevel = 1;
        }
        else
        {
          if (waktuTampil == LihatWaktuTampil)
          {
            if (modeMenu == modeMenuMakanan)
            {
              menuMakanan[indexMenu].pilihan = false;
            }
            else if (modeMenu == modeMenuMinuman)
            {
              menuMinuman[indexMenu].pilihan = false;
            }
            updateMenu();
          }
          else
          {
            waktuTampil = LihatWaktuTampil;
            millisMulai = millis();
          }
        }
        break;
      default:
        indexMenu = key - '1';
        updateMenu();
        millisMulai = millis();
        waktuTampil = LihatWaktuTampil;
        break;
    }
  }
 
  if (millis() - millisMulai > waktuTampil)
  {
    millisMulai = millis();
    waktuTampil = standarWaktuTampil;
    indexMenu++;
    updateMenu();
    menuLevelSebelumnya = menuLevel;
  }
}
void pesananMasuk()
{
  //Aksi pesanan masuk
  lcd.setCursor(0, 0);
  lcd.println("  Terima Kasih  ");
  lcd.setCursor(0, 1);
  lcd.println("Silahkan tunggu ");
  delay(3000);
  Serial.println("Pesanan masuk!!!");
  menuLevel = 0;
  indexMenu = -1;
}
void updateMenu()
{
  if (menuLevel == 0)
  {
    if (indexMenu == sizeof(menuUtama) / sizeof(menuUtama[0]))
    {
      indexMenu = 0;
    }
    if (menuLevelSebelumnya != menuLevel)
    {
      lcd.clear();
      lcd.print("Selamat Datang");
    }
    tampilMenuUtama(indexMenu);
  }
  else if (menuLevel == 2)
  {
    lcd.setCursor(0, 0);
    lcd.print("Rp. ");
    formatStrHarga(totalPilihan());
    lcd.setCursor(0, 1);
    lcd.print("* Ya   # kembali");
  }
  else if (modeMenu == modeMenuMakanan)
  {
    if (indexMenu >= jumlahMakanan)
    {
      indexMenu = 0;
    }
    tampilMenuMakanan(indexMenu);
  }
  else if (modeMenu == modeMenuMinuman)
  {
    if (indexMenu >= jumlahMinuman)
    {
      indexMenu = 0;
    }
    tampilMenuMinuman(indexMenu);
  }
}
void resetPilihan()
{
  for (byte i = 0; i < jumlahMakanan; i++)
  {
    menuMakanan[i].pilihan = false;
  }
  for (byte i = 0; i < jumlahMinuman; i++)
  {
    menuMinuman[i].pilihan = false;
  }
}
uint32_t totalPilihan()
{
  uint32_t total = 0;
  for (byte i = 0; i < jumlahMakanan; i++)
  {
    if (menuMakanan[i].pilihan)
    {
      total += menuMakanan[i].harga;
    }
  }
  for (byte i = 0; i < jumlahMinuman; i++)
  {
    if (menuMinuman[i].pilihan)
    {
      total += menuMinuman[i].harga;
    }
  }
  return total;
}
void tampilMenuUtama(byte index)
{
  lcd.setCursor(0, 1);
  lcd.print(menuUtama[index]);
}
void tampilMenuMakanan(byte index)
{
  lcd.setCursor(0, 0);
  lcd.print(menuMakanan[index].strMenu);
  lcd.setCursor(0, 1);
  lcd.print("Rp. ");
  formatStrHarga(menuMakanan[index].harga);
  if (menuMakanan[index].pilihan)
  {
    lcd.setCursor(15, 1);
    lcd.print("*");
  }
}
void tampilMenuMinuman(byte index)
{
  lcd.setCursor(0, 0);
  lcd.print(menuMinuman[index].strMenu);
  lcd.setCursor(0, 1);
  lcd.print("Rp. ");
  formatStrHarga(menuMinuman[index].harga);
  if (menuMinuman[index].pilihan)
  {
    lcd.setCursor(15, 1);
    lcd.print("*");
  }
}
void formatStrHarga(uint32_t harga)
{
  String strHarga = String(harga);
  uint8_t panjangStr = strHarga.length();
  uint8_t offset = 3 - (panjangStr % 3);
  for (byte i = 0; i < strHarga.length(); i++)
  {
    lcd.print(strHarga[i]);
    if (!((strHarga.length() + i - offset + 1) % 3))
    {
      if (i != strHarga.length() - 1)
      {
        lcd.print('.');
      }
    }
  }
  for (byte i = 0; i < 16 - 5 - strHarga.length(); i++)
  {
    lcd.print(' ');
  }
}

Library:

Simpan dan ambil data setting menggunakan metode list pada App Inventor

Database tinyDB

Cara menyimpan data input app inventor agar dapat ditampilkan kembali saat app dibuka kembali adalah dengan memanfaatkan media storage seperti TinyDB untuk menyimpan data tersebut. Data-data yang disimpan dalam database TinyDB diberi index/tag tertentu. Untuk mengambil data dari TinyDB dibutuhkan input/tag yang sesuai.

Kegunaan menyimpan data ke dalam database TinyDB adalah supaya data yang telah di entri tidak hilang setelah app ditutup.

List interface / object

Penggunaan interface dalam jumlah besar pada App Iventor sebaiknya menggunakan list, karena dengan list proses pengambilan dan penyimpanan hasil lebih mudah dan memperpendek block.

berikut keterangan block perancangan apk App Inventor:

Tahap awal penggunaan list adalah dengan menginisialisasi-nya sebagai list kosong (empty list) masing-masing kelompok interface (seperti checkbox dan textbox)

Kemudian list-list tersebut ditambahkan item yang bersesuaian, dalam contoh ini checkbox1, checkbox2, checkbox3 ditambahkan dalam list Array_checkbox. dan textbox1, textbox2, textbox3 ditambahkan dalam list Array_textbox.

Selanjutnya prosedur ambil_dari_database dipanggil untuk mengambil data-data yang tersimpan sebelumnya ke dalam masing-masing item list.

Prosedur ini menggunakan metode list dengan memecah item-item dalam variabel Array_checkbox dan Array_textbox.

Prosedur ambil data dari database akan melaksanakan fungsi mengambil data-data dari tinyDB dengan tag checkbox1, checkbox2, checkbox3 dan textbox1, textbox2, textbox3.

Prosedur Simpan_ke_database berfungsi menyimpan data-data yang telah di entry  kedalam database dengan tag/index tertentu.

Contoh penggunaan prosedur simpan_ke_database adalah saat pengguna / user menekan tombol back. Sebelum keluar dari app, terlebih dahulu dipanggil prosedur Simpan_ke_database untuk menyimpan data-data entri interface untuk digunakan kembali saat app dibuka selanjutnya.

blok app Inventor cara menyimpan data dan cara menggunakan list yang digunakan :

Design screen app inventor dengan banyak interface serupa yang di kelompokkan kedalam list yang berguna mempersingkat perulangan block.

dan apk cara menggunakan database app Inventor yang dipakai dalam contoh ini:

SimpanDanAmbilDataSetting.apk

Tampilan seven segment dengan metode scanning menggunakan arduino

Seven segment banyak digunakan sebagai tampilan dari sebuah perangkat, terutama tampilan angka. Seven segment telah tersedia dalam berbagai macam teknologi serta kelebihan masing-masing. Seven segment jenis LED memiliki kelebihan mampu menghasilkan cahaya sendiri sehingga terlihat lebih terang. Sedangkan seven segmen LCD hanya membutuhkan daya kecil untuk operasi-nya.

Setiap segment dari 7segment memiliki paling kurang 8 pin yaitu 7 pin untuk digit, dan 1 pin untuk CA/CC. Untuk membentuk deret angka seven segment digabungkan menjadi beberapa digit. Untuk mengontrol banyak digit seven segment biasanya digunakan metode scanning. Melalui metode scanning ini penggunaan pin menjadi lebih efisien.

Cara menghubungkan seven segment dengan arduino bisa menggunakan transistor array dan BCD to seven segment. Apabila menggunakan seven segment ukuran kecil misalnya ukuran 0.5″, bisa saja seven segment-nya dihubungkan langsung ke arduino seperti contoh berikut:

skema rangkaian seven segment menggunakan arduino:

tata letak seven segment:

sketch atau program scanning seven segment menggunakan arduino:

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
#include <TimerOne.h>
 
#define CCorCA            1//0 = CC, 1 = CA
#define jumlah7Segment    4
 
#define pin1    A0
#define pin2    A1
#define pin3    A2
#define pin4    A3
 
#define pinA    4
#define pinB    5
#define pinC    6
#define pinD    7
#define pinE    8
#define pinF    9
#define pinG    10
#define pinDot  11
 
byte pin7Segment[] = {pinA, pinB, pinC, pinD, pinE, pinF, pinG};
byte pin7SegmentCommon[] = {pin1, pin2, pin3, pin4};
 
const char angka[] = {
  0b00111111,
  0b00000110,
  0b01011011,
  0b01001111,
  0b01100110,
  0b01101101,
  0b01111100,
  0b00000111,
  0b01111111,
  0b01100111,
  0b00000000,//blank
  0b01000000,//strip
  0b01101101,//S
  0b00000100,//i
  0b01011111,//a
  0b01110011,//p
 
};
#define Seg_blank 10
#define Seg_stip 11
#define Seg_S 12
#define Seg_i 13
#define Seg_a 14
#define Seg_p 15
 
#define dotBlank 6
#define dotAll   5
 
volatile byte nilai7Segment[4];
volatile byte lastScanIndex = 0;
volatile byte index7Segment = 0;
volatile byte posisiTitik;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Tampilan 7 Segment metode scanning menggunakan arduino");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);
 
  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pinC, OUTPUT);
  pinMode(pinD, OUTPUT);
  pinMode(pinE, OUTPUT);
  pinMode(pinF, OUTPUT);
  pinMode(pinG, OUTPUT);
  pinMode(pinDot, OUTPUT);
 
  Timer1.initialize(2000);
  Timer1.attachInterrupt( timerIsr );
 
  nilai7Segment [3] = Seg_S;
  nilai7Segment [2] = Seg_i;
  nilai7Segment [1] = Seg_a;
  nilai7Segment [0] = Seg_p;
  posisiTitik = dotBlank;
  delay(1000);
}
 
void loop() {
  for (uint16_t i = 0; i < 10000; i++)//0 .. 99.99
  {
    uint32_t BCD = Convert_IntToBCD32(i);
    nilai7Segment [0] = (BCD >> 0) & 0x0F;
    nilai7Segment [1] = (BCD >> 4) & 0x0F;
    nilai7Segment [2] = (BCD >> 8) & 0x0F;
    nilai7Segment [3] = (BCD >> 12) & 0x0F;
    posisiTitik = 2;
    delay(100);
  }
}
 
void timerIsr()
{
  digitalWrite(pin7SegmentCommon[lastScanIndex], CCorCA ? LOW : HIGH);
  drive7Segment(nilai7Segment[index7Segment]);
  if (posisiTitik == dotAll)
  {
    digitalWrite(pinDot, CCorCA ? LOW : HIGH);
  }
  else if (posisiTitik == dotBlank)
  {
    digitalWrite(pinDot, CCorCA ? HIGH : LOW);
  }
  else
  {
    digitalWrite(pinDot, (index7Segment == posisiTitik) ? CCorCA ? LOW : HIGH : CCorCA ? HIGH : LOW);
  }
  digitalWrite(pin7SegmentCommon[index7Segment], CCorCA ? HIGH : LOW);
  lastScanIndex = index7Segment++;
  if (index7Segment >= jumlah7Segment)index7Segment = 0;
}
void drive7Segment(byte nilai)
{
  byte nilai7Segment = CCorCA ? ~angka[nilai] : angka[nilai];
  for (byte i = 0; i < sizeof(pin7Segment); i++)
  {
    digitalWrite(pin7Segment[i], nilai7Segment & 0x01);
    nilai7Segment >>= 1;
  }
}
uint32_t Convert_IntToBCD32(uint32_t DecimalValue)
{
  uint32_t returnValue = 0;
  //uint32_t LSB_L = DecimalValue;
 
  while (DecimalValue >= 10000000L)
  {
    DecimalValue -= 10000000L;
    returnValue += 0x10000000;
  }
  while (DecimalValue >= 1000000L)
  {
    DecimalValue -= 1000000L;
    returnValue += 0x01000000;
  }
  while (DecimalValue >= 100000L)
  {
    DecimalValue -= 100000L;
    returnValue += 0x00100000;
  }
  while (DecimalValue >= 10000)
  {
    DecimalValue -= 10000;
    returnValue += 0x00010000;
  }
  while (DecimalValue >= 1000L)
  {
    DecimalValue -= 1000L;
    returnValue += 0x00001000;
  }
  while (DecimalValue >= 100)
  {
    DecimalValue -= 100;
    returnValue += 0x00000100;
  }
  while (DecimalValue >= 10)
  {
    DecimalValue -= 10;
    returnValue += 0x00000010;
  }
  return returnValue + DecimalValue;
}

Library : TimerOne.zip

Kalibrasi sensor loadcell interaktif dengan Serial USB TTL Arduino

Sebelum digunakan, timbangan wajib ditera atau di kalibrasi. Proses kalibrasi timbangan dilakukan dengan membandingkan hasil terukurnya dengan berat diketahui sebuah beban pengkalibrasinya.

Loadcell digunakan sebagai sensor berat digital. Proses kalibrasi loadcell dengan arduino menggunakan metode dua beban menggunakan formula :

cara mengkalibrasi loadcell dengan metode dua beban ini menghasilkan dua variabel yaitu skala dan ofset.

Timbangan digital dengan sensor loadcell memiliki karakteristik akurasi berikut :

  1. Skala
  2. Ofset
  3. Linearitas
  4. Diferensial linearitas
  5. Kuantisasi
  6. Transisi akurasi

Setiap loadcell memiliki karakteristik yang berbeda-beda. cara memperoleh nilai skala dan offset yang baik adalah dengan menetapkan nilai beban pengkalibrasi antara 25% – 75% dari kapasitas loadcell.

skema / rangkaian kalibrasi loadcell arduino:

Setting kalibrasi ini akan disimpan di EEPROM, dan akan diambil kembali saat perangkat mulai dihidupkan.

koding /program kalibrasi loadcell menggunakan arduino:

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
#include "HX711.h"
#include <EEPROM.h>
 
#define alamatKalibrasiM 0
#define alamatKalibrasiC 4
 
//pin
HX711 scale(A0, A1); // (DT, SCK)
 
byte modeKalibrasi = 0;
uint16_t beratKalibrasi1Tera;
uint16_t beratKalibrasi2Tera;
long beratKalibrasi1;
long beratKalibrasi2;
 
long lastMillis;
 
void setup() {
  Serial.begin(9600);
  Serial.println("Kalibrasi Loadcell");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
   
  float m,c;
  EEPROM.get(alamatKalibrasiM, m);
  EEPROM.get(alamatKalibrasiC, c);
  scale.power_up();
  scale.set_scale(m);
  scale.set_offset(c);
  scale.power_down();
 
  lastMillis = millis();
}
 
void loop() {
  if(Serial.available())
  {
    if(modeKalibrasi == 0)
    {
      if(toupper(Serial.read()) == 'K')
      {
        Serial.println("Masukkan beban kalibrasi pertama (gram) :");
        modeKalibrasi = 1;
      }
    }
    else if (modeKalibrasi == 1)
    {
      scale.power_up();
      delay(100);
      beratKalibrasi1Tera = Serial.parseInt();
      beratKalibrasi1 = scale.read_average(10);
      Serial.println("Beban = " + String(beratKalibrasi1Tera) + " gram, terukur = " + String(beratKalibrasi1) + " unit");
      Serial.println("Masukkan beban kalibrasi kedua yang lebih besar (gram) :");
      modeKalibrasi = 2;
      scale.power_down();
    }
    else if (modeKalibrasi == 2)
    {
      scale.power_up();
      delay(100);
      beratKalibrasi2Tera = Serial.parseInt();
      beratKalibrasi2 = scale.read_average(10);
      Serial.println("Beban = " + String(beratKalibrasi2Tera) + " gram, terukur = " + String(beratKalibrasi2) + " unit");
       
      float m = 1.0 * (beratKalibrasi2 - beratKalibrasi1) / ( beratKalibrasi2Tera - beratKalibrasi1Tera);
      float c = beratKalibrasi2 - (1.0 * m * beratKalibrasi2Tera);
 
      scale.set_scale(m);
      scale.set_offset(c);
      EEPROM.put(alamatKalibrasiM, m);
      EEPROM.put(alamatKalibrasiC, c);
 
      Serial.print("Skala = ");
      Serial.println(m);
      Serial.print("Ofset = ");
      Serial.println(c);
 
      scale.power_down();
      Serial.println("Kalibrasi berhasil.");
       
      modeKalibrasi = 0;
    }
  }
 
  if(!modeKalibrasi)
  {
    if(millis() - lastMillis > 100)
    {
      scale.power_up();
      delay(10);
      float berat = scale.get_units(10);
      scale.power_down();
      Serial.print("Berat : ");
      Serial.println(berat);
      lastMillis = millis();
    }
  }
}

Cara kalibrasi loadcell melalui serial monitor:

Prosedur kalibrasi loadcell dengan arduino melalui serial monitor:

  1. Input ‘K’ atau ‘k’ untuk masuk mode kalibrasi.
  2. Naikkan beban pengkalibrasi pertama
  3. Inputkan berat tera beban pengkalibrasi pertama – Enter
  4. Naikkan beban pengkalibrasi kedua
  5. Inputkan berat tera beban pengkalibrasi kedua – Enter
  6. Selesai

Library : HX711.zip

Seting waktu RTC otomatis dengan arduino

DS1307 dan DS3231 merupakan RTC (real time clock) yang umum digunakan dalam perancangan arduino. kedua RTC ini saling kompatibel untuk register waktunya, sedangkan register kontrol memiliki alamat berbeda. Selain itu DS3231 memiliki kelebihan dua alarm yang tidak dimiliki oleh DS1307.

Penggunaan RTC pertama kali, RTC harus disetting terlebih dahulu, cara setting DS3231 adalah dengan memberi nilai pada register waktunya, juga register kontrol atau register alarm (khusus DS3231).

Cara menyetting RTC DS3231 dan DS1307 umumnya membutuhkan dua sketch untuk menggunakan RTC ini yaitu setTime dan program utama. Program ‘set waktu’ harus dipisah untuk menghindari program me-‘set waktu’ setiap kali arduino reset.

Otomatisasi setting RTC bertujuan untuk membuat setting waktu berada dalam sketch utama sehingga lebih praktis terutama jika sketch akan bagikan ke publik atau pengguna lain.

Untuk mengetahui apakah RTC sudah disetting atau belum, digunakan 1 byte EEPROM untuk menyimpan status (dibaca tokenRTC), jika RTC sudah disetting maka sketch akan menulis dialamat alamatEEPROMCekToken pada EEPROM dengan nilai tokenRTC.

Ketika program dijalankan sketch akan menguji nilai tokenRTC, jika sama maka sketch tidak lagi melakukan setting waktu RTC.

Skema tulis waktu rtc otomatis (kompatibel DS1307):

sketch/program Atur waktu RTC otomatis (bisa digunakan langsung untuk DS1307 dan DS3231 tanpa library):

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
#include <EEPROM.h>
#include <Wire.h>
#include <Sodaq_DS3231.h>
 
#define alamatRTC 0x68
#define alamatEEPROMCekToken 0
#define tokenRTC 0xAA //<== rubah token jika ingin nilai baru
 
void setup() {
  Serial.begin(9600);
  Serial.println("Set waktu RTC otomatis dengan arduino");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  Wire.begin();
  if (EEPROM.read(alamatEEPROMCekToken) != tokenRTC)
  {
    //Waktu compiler
    char bulan[12];
    byte indexBulan;
    int jam, menit, detik, tanggal, tahun;
 
    char *namaBulan[12] = {
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    sscanf(__TIME__, "%d:%d:%d", &jam, &menit, &detik);
    sscanf(__DATE__, "%s %d %d", bulan, &tanggal, &tahun);
    for (indexBulan = 0; indexBulan < 12; indexBulan++) {
      if (strcmp(bulan, namaBulan[indexBulan]) == 0)
        break;
    }
    uint8_t wday = hariDariTanggal(tanggal, indexBulan + 1, tahun);
    DateTime dt(tahun, indexBulan + 1, tanggal, jam, menit, detik, wday);
    rtc.setDateTime(dt);
    EEPROM.write(alamatEEPROMCekToken, tokenRTC);
    Serial.println("RTC sudah otomatis di setting (Sekali saja)");
  }
}
 
uint32_t old_ts;
void loop() {
  String strNamaHari[] = {"Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jum'at", "Sabtu"};
  DateTime now = rtc.now(); //get the current date-time
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.date(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.print(' ');
  Serial.print(strNamaHari[now.dayOfWeek()-1]);
  Serial.println();
 
  while (1);
}
byte hariDariTanggal(byte tanggal, byte bulan, uint16_t tahun)
{
  uint16_t jumlahHariPerBulanMasehi[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  if (tahun >= 2000)
    tahun -= 2000;
 
  uint32_t jumlahHari = tahun * 365;
  uint16_t tahunKabisat = tahun / 4;
  for (byte i = 0; i < tahun; i++)
  {
    if (!(i % 4))
    {
      jumlahHari++;
    }
  }
  jumlahHari += jumlahHariPerBulanMasehi[bulan - 1];
  if ( (bulan >= 2) && !(tahun % 4))
  {
    jumlahHari++;
  }
  jumlahHari += tanggal;
  return ((jumlahHari + 5) % 7) + 1;
}

Library: Sodaq_DS3231.zip
Versi tanpa library : set_otomatis_rtc.ino

Konversi sistem bilangan biner ke BCD

Bilangan biner adalah angka berbasis 2 yang hanya memiliki numerik ‘0’ dan ‘1’. Dalam ilmu komputer satu numerik bilangan biner disebut “bit”, apabila bilangan ini dikelompokkan kedalam 8 bit disebut dengan byte, Satu byte bisa menyimpan kombinasi hingga 2^8 atau 256 kombinasi.

Dalam ilmu komputer juga dikenal bilangan hex (hexadesimal) yaitu angka berbasis 16 dengan numerik 0-9 dan A-F, bilangan ini merepresentasikan 4-bit bilangan biner (binary number).

Bilangan biner atau bilangan hex juga bilangan byte umum digunakan dalam perhitungan digital dan komputer, namun bilangan ini sulit dicerna oleh manusia yang menggunakan bilangan desimal (basis 10),

Bilangan BCD (binary coded decimal) adalah bilangan hex dalam bentuk desimal yang lebih familier dan bisa terbaca oleh manusia dalam bentuk desimal.

byte 8-bit binary ke BCD

1
2
3
4
5
//max hex 0x63 / bcd 0x99
uint8_t Convert_IntToBCD(uint8_t DecimalValue)
{
    return DecimalValue + 6 * (DecimalValue / 10);
}

atau

1
2
3
4
5
6
7
8
9
10
11
12
13
//max hex 0x63 / bcd 0x99
uint8_t Convert_IntToBCD(uint8_t DecimalValue)
{
    uint8_t MSB = 0;
    uint8_t LSB = DecimalValue;
 
    while (LSB >= 10)
    {
        LSB -= 10;
        MSB += 0x10;
    }
    return MSB + LSB;
}

word 16-bit binary ke BCD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//max hex 0x270F / bcd 0x9999
uint16_t Convert_IntToBCD16(uint16_t DecimalValue)
{
    uint16_t returnValue = 0;
    //uint16_t LSB_L = DecimalValue;
 
    while (DecimalValue >= 1000)
    {
        DecimalValue -= 1000;
        returnValue += 0x1000;
    }
    while (DecimalValue >= 100)
    {
        DecimalValue -= 100;
        returnValue += 0x0100;
    }
    while (DecimalValue >= 10)
    {
        DecimalValue -= 10;
        returnValue += 0x0010;
    }
    return returnValue + DecimalValue;
}

integer 32-bit binary ke BCD

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
//max hex 0x5F5E0FF / bcd 0x99999999
uint32_t Convert_IntToBCD32(uint32_t DecimalValue)
{
    uint32_t returnValue = 0;
    //uint32_t LSB_L = DecimalValue;
 
    while (DecimalValue >= 10000000L)
    {
        DecimalValue -= 10000000L;
        returnValue += 0x10000000;
    }
    while (DecimalValue >= 1000000L)
    {
        DecimalValue -= 1000000L;
        returnValue += 0x01000000;
    }
    while (DecimalValue >= 100000L)
    {
        DecimalValue -= 100000L;
        returnValue += 0x00100000;
    }
    while (DecimalValue >= 10000)
    {
        DecimalValue -= 10000;
        returnValue += 0x00010000;
    }
    while (DecimalValue >= 1000L)
    {
        DecimalValue -= 1000L;
        returnValue += 0x00001000;
    }
    while (DecimalValue >= 100)
    {
        DecimalValue -= 100;
        returnValue += 0x00000100;
    }
    while (DecimalValue >= 10)
    {
        DecimalValue -= 10;
        returnValue += 0x00000010;
    }
    return returnValue + DecimalValue;
}

Jadwal Waktu Shalat (JWS) 2 & 3 Panel P10 Arduino Uno dengan kontroller Android melalui Bluetooth

Rancangan displai jadwal waktu sholat / JWS menggunakan arduino ini memanfaatkan I2C EEPROM 24C32 sebagai penyimpan data tampilan Infomasi text yang mampu menampung 4096 karakter. Chip 24C32 umumnya disertakan pada modul RTC DS1307 / DS3231 dengan jalur komunikasi yang sama dan alamat yang berbeda. Karena komunikasi I2C dikategorikan lambat, maka akses pembacaan memori ini diminimalisir dengan metode read on demand.

Display Jadwal Sholat Arduino

Perancangan JWS menggunakan Arduino atau JWS dengan kontroller running text harus mampu menangani hal-hal berikut :

  1. Waktu sholat berdasarkan perjalanan matahari yang berubah-ubah sepanjang tahun, sehingga dibutuhkan perhitungan khusus untuk menentukan posisi matahari secara matematis. Formula yang digunakan berasal dari islamicity, dengan penambahan variabel sudut-sudut matahari.
  2. Pengaturan yang komplek namun fleksibel mengharuskan JWS didesain mampu menyesuaikan tugasnya dengan beberapa parameter yang bisa di monitor atau diganti berkala. Seperti adanya kegiatan-kegiatan khusus sehingga JWS bisa beralih fungsi.

Kapasitas memory flash arduino uno (ATMega328) yang 32Kb masih menyisakan ruang untuk 3 set font diamping 1 font angka. Font-font ini berfungsi memberikan efek tulisan sehingga memungkinkan adanya penekanan informasi.

Efek JWS Arduino

Efek-efek tampilan diperlukan supaya kehadiran JWS lebih menghiasi dinding, bukan hanya sebagai papan informasi. efek yang di tambahkan :

  1. Wipe in – wipe out
  2. Marque
  3. Lukis gambar

JWS Bluetooth

Fasilitas pengaturan JWS dengan bluetooth:

  1. 10 teks informasi yang disimpan dalam memory 24C32 yang mampu menyimpan 4096 karakter.
  2. Font informasi bisa diatur terpisah (System Font 5×7,Arial 14,Arial Black 16) pada uno, dan bisa ditambahkan pada board yang memiliki memori lebih besar.
  3. Waktu padam dan nyala.
  4. Perhitungan waktu sholat dengan metode sudut matahari.
  5. Tampilan tanggal hijriah (bisa diatur dan dinonaktifkan).
  6. Efek wipe untuk tampilan waktu-waktu sholat.
  7. Efek tulisan gambar.
  8. Pengaturan bluetooth meliputi kecerahan DMD panel, kecepatan scroll.
  9. Token aplikasi apk, untuk mengantisipasi apabila ada komunikasi bluetooth yang tidak diinginkan yang mungkin akan merubah setting.
  10. Konfirmasi data setting diterima dan waktu padam/nyala ke apk android.
  11. Data yang diketik di apk disimpan dalam database android, jadi saat aplikasi ditutup data yang diketik tetap akan tampil saat aplikasi dibuka kembali.

Komponen yang digunakan dalam JWS Arduino bluetooth:

  1. Arduino Uno (Gunakan Arduino mega untuk memory yang lebih besar).
  2. RTC DS3231 (termasuk 24C32) kompatibel DS1307 (beda address)
  3. Bluetooth HC-05
  4. Panel P10 (2 panel)

Jika ingin upload sketch dari arduino ide, lepaskan dahulu pin ‘0’ / pin RX jika menggunakan bluetooth hardware serial.

Skema arduino JWS bluetooth:

Video bluetooth arduino JWS:

 

JWS Android

Screenshoot JWS Android bluetooth:

JWS bluetooth android ini menggunakan hardware SPI sebagai pengirim data ke running text, meskipun mengorbankan pin 12 (MISO) yang tidak bisa digunakan karena dikontrol oleh SPI.

Pengaturan JWS dengan arduino yang dibutuhkan :

  1. #define modeDemo pada baris atas digunakan jika ingin menjalankan JWS demo, hapus baris ini jika JWS digunakan normal.
  2. Pin Kontrol DMD hardware SPI : pinOE  harus dipasangkan pada pin PWM sebagai pengatur kecerahan. Sedangkan pinA, pinB, dan pinSCK bisa ditempatkan dimana saja.
  3. Jumlah panel.
  4. Perancangan ini dibuat dengan DS3231, jika menggunakan DS1307 harap diganti bagian alamat I2C-nya.
  5. Pin SQW (DS3231) diset sebagai penghasil pulsa dengan frekuensi 1 HZ sebagai interupsi bagi JWS bahwa detik telah berubah. dalam perancangan JWS arduino ini dihubungkan ke pin A3 namun bisa dipindahkan kemana saja.
  6. Jenis font bisa disesuaikan dengan kebutuhan, (#include-kan juga library font-nya). dengan merubah variabel berikut:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #define fontWaktu               angka6x13
    #define fontInformasi           SystemFont5x7Ramping
    #define fontTanggal             SystemFont5x7Ramping
    #define fontNamaSholat          SystemFont5x7Ramping
    #define fontWaktuSholat         SystemFont5x7Ramping
    #define fontWaktuMasuk          SystemFont5x7Ramping
    #define fontAdzan               SystemFont5x7Ramping
    #define fontIqomah              SystemFont5x7Ramping
    #define fontMatikanHP           Arial14
  7. JWS ini dilengkapi sistem token, untuk RTC dan komunikasi bluetooth, apabila token yang digunakan cocok maka perintah untuk RTC dan bluetooth (setting variabel) akan dijalankan.

Sketch/koding header dan setup android bluetooth JWS:

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
#define modeDemo                0    //<<==================================== DEMO prinsip kerja 0 / 1
 
#define selisihImsak            10
 
//defenisi pin
#define pinA                    6
#define pinB                    7
#define pinOE                   9
#define pinSCK                  8
 
#define alarm                   5
#define RTCDetikIRQ             A3
 
//variabel Setting
#define jumlahKolomPanel        2  //panel
#define jumlahBarisPanel        1  //panel
 
#define lamaTampilWaktuSholat   10 // * 100 milidetik
#define lamaTampilInformasiJam  100 // * 100 milidetik
 
#define fontWaktu               angka6x13
#define fontInformasi           SystemFont5x7Ramping
#define fontTanggal             SystemFont5x7Ramping
#define fontJadwalSholat        SystemFont5x7Ramping
#define fontWaktuSholat         SystemFont5x7Ramping
#define fontWaktuMasuk          SystemFont5x7Ramping
#define fontAdzan               SystemFont5x7Ramping
#define fontIqomah              SystemFont5x7Ramping
#define fontMatikanHP           Arial14
 
#define RTC_ADDRESS             0x68
#define I2CEEPROM_ADDRESS       0x57  //Perhatikan alamat I2CEEPROM 0x50(ds1307 .. 0x57(ds3231)
#define RTC_CONTROL_REG         0x0E  //0x07 ds1307, 0x0E ds3231
 
 
#include <Wire.h>
#include <SPI.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <SoftwareSerial.h>
 
#include <DMD2.h>
 
#include "fungsi.h"
#include "definisi.h"
#include "konstanta.h"
#include "rtc.h"
#include "prayer.h"
#include "i2cUmum.h"
#include "trigonometri.h"
 
#include <fonts/SystemFont5x7Ramping.h>
#include <fonts/Arial14.h>
#include <fonts/Arial_Black_16.h>
#include <fonts/angka6x13.h>
#include <fonts/AllahMuhammad.h>
 
const uint16_t alamatFont[] = {&SystemFont5x7Ramping, &Arial14, &Arial_Black_16};
 
struct Setting
{
  int8_t koreksiHijriah;
  int8_t jumlahAlarm;
  int8_t lamaAdzan;
  int8_t lamaIqomah[5];
  int8_t ulangMatikanHP;
  int8_t lamaSholat[5];
  int8_t lamaSholatJumat;
  int8_t kecepatanJalanInfoText;
  int8_t kecerahanJWS;
  int8_t padamJam;
  int8_t padamMenit;
  int8_t nyalaJam;
  int8_t nyalaMenit;
  int8_t tampilanHijriah;
  int8_t tampilanImsak;
  int8_t tampilanTerbit;
  int8_t tampilanDhuha;
  int8_t tampilanTerbenam;
  int8_t JumlahInfoTextAktif;
};
 
#if modeDemo
  Setting setting = {0, 5, 1, {5, 5, 5, 5, 5}, 1, {5, 5, 5, 5, 5}, 2, 50, 20, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1};
  char namaMesjid[panjangString] = "Musholla ...\0   ";
#else
  Setting setting;
  char namaMesjid[panjangString];
#endif
 
//variabel
const char strWaktu[] PROGMEM = "Waktu";
const char strSholat[] PROGMEM = "Sholat";
char strWaktuInformasi[9];
JamDanMenit waktuSholat[9];
 
EfekMarqueHorizontalkeKiri marqueKiri;
EfekTulis efekTulis;
uint8_t efekScrollOutDanWipeIn;
 
uint8_t marqueOut;
bool marqueTampilan;
uint8_t wipeInTampilan;
uint8_t wipeOutTampilan;
uint8_t efekTulisTampilan;
 
 
uint8_t indexNamaSholat;
uint16_t hitungMundurSholat;
uint16_t hitungMundurPesanSebelumSholat;
uint16_t hitungMundurIqomah;
uint16_t hitungMundurAdzan;
uint8_t hitungMundurAlarm;
uint16_t hitungMudurAlarmIqomah;
 
byte modeOperasi;
byte modeOperasiSebelumnya;
 
uint8_t menitSebelumnya = 60;
uint8_t hariSebelumnya = 32;
 
bool statusAlarm;
 
bool tampilkanMenit;
bool infoWaktu;
byte infoWaktuTop;
uint16_t lamaTampil = 0;
uint16_t indekTampilan;
 
char textWaktuJam[6];
char textTanggalHijriah[sizeof(strNamaHari[0]) + 2 + 3 + sizeof(strNamaBulanHijriah[0]) + 3 - 2];
 
uint8_t bluetoothData[32];
uint16_t indexParseSerial = 0;
 
byte indexInformasi = 1;
long mulaiMillis;
uint16_t AlamatI2CEEPROM;
 
int8_t indexSetting = 0;
byte indexSettingInfo;
bool tokenValid = false;
bool RTCValid = true;
 
byte tanggalHijriah;
byte bulanHijriah;
uint16_t tahunHijriah;
 
uint16_t AlamatI2CEEPROMSebelumnya;
bool nextEEPROMData = false;
byte EEPROMDataCounter;
 
bool modeSambungSebelumnya = false;
 
tanggalDanWaktu now;
tanggalDanWaktu settingWaktu;
byte settingWaktuIndex;
 
//Class variabel
#define l jumlahKolomPanel * 32
#define t jumlahBarisPanel * 16
SPIDMD dmd(jumlahKolomPanel, jumlahBarisPanel, pinOE, pinA, pinB, pinSCK);
DMDFrame dmdEfek(l, t);
 
DMD_TextBox boxKolom1(dmd, 0, 0, l / 2, t);
DMD_TextBox boxKolom2(dmd, l / 2, 0, l / 2, t);
DMD_TextBox boxBaris1(dmd, 0, 0, l, (t / 2) - 1);
DMD_TextBox boxBaris2(dmd, 0, t / 2, l, t / 2);
 
DMD_TextBox boxKolom2Baris1(dmd, l / 2, 0, l / 2, t / 2);
DMD_TextBox boxKolom2Baris2(dmd, l / 2, t / 2, l / 2, t / 2);
 
//Eeprom24C32_64 I2CEEPROM(EEPROM_ADDRESS);
HardwareSerial *bluetooth;
 
#define tokenRTC 0xA9
#define tokenBluetooth 0x4C
#define tokenSetInformasi "225654"
#define tokenSetSetting "963215"
 
void setup() {
  Serial.begin(9600);
  Serial.println("JWS Bluetooth");
  Serial.println("https://www.project.semesin.com/");
  Serial.println();
 
  bluetooth = &Serial;
 
  pinMode(alarm, OUTPUT);
  pinMode(RTCDetikIRQ, INPUT_PULLUP);
 
  //Interupsi setiap 1 detik
  Wire.begin();
  Wire.beginTransmission(RTC_ADDRESS);
  Wire.write(RTC_CONTROL_REG);
  Wire.write(0x00);//SQW freq, 1Hz
  Wire.endTransmission();
 
#if modeDemo
  prayerData = { -1576, 27343, 1792, 5120, 213, -1152, 1024, 4608, 0, 0, 0,0,0};
  settingWaktu = {50, 29, 4, 5, 7, 5, 2018};
  simpanRTC(RTC_ADDRESS, settingWaktu);
#endif
 
  if (EEPROM.read(alamatTokenRTC) != tokenRTC)
  {
    //Waktu compiler
    char Month[12];
    int Day, Year;
    uint8_t monthIndex;
    int Hour, Min, Sec;
    char *monthName[12] = {
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    sscanf(__TIME__, "%d:%d:%d", &Hour, &Min, &Sec);
    sscanf(__DATE__, "%s %d %d", Month, &Day, &Year);
    for (monthIndex = 0; monthIndex < 12; monthIndex++) {
      if (strcmp(Month, monthName[monthIndex]) == 0) break;
    }
    uint8_t wday = hariDariTanggal(Day, monthIndex + 1, Year);
    settingWaktu = {Sec, Min, Hour, wday, Day, monthIndex + 1, Year};
    simpanRTC(RTC_ADDRESS, settingWaktu);
    EEPROM.write(alamatTokenRTC, tokenRTC);
  }
 
#if !modeDemo
  if (EEPROM.read(alamatTokenBluetooth) == tokenBluetooth)
  {
    ambilSetting();
  }
#endif
  ambilHijriah();
  dmd.begin();
 
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
 
  mulaiMillis = millis();
  modeOperasi = tampilWaktuSholat;
 
  while (!digitalRead(RTCDetikIRQ));
}

 

File-file yang digunakan dalam JWS android :

  1. definisi.h
  2. fungsi.h
  3. i2cUmum.h
  4. JWS_Semesin.ino
  5. konstanta.h
  6. prayer.h
  7. rtc.h
  8. trigonometri.h
  9. JWS_Bluetooth.apk
  10. file lengkap (.zip)

Rekomendasi Perakitan JWS Bluetooth Android:

  1. unzip JWS Bluetooth + apk – Semesin.zip (adalah folder sketch arduino beserta file pendukung dalam satu folder)
  2. copy isi folder libraries ke Document/Arduino/libraries/
  3. Buka JWS_Semesin.ino dengan arduino IDE (pengujian dengan versi 1.8.5)
  4. Lepas tx Bluetooth
  5. Upload ke arduino
  6. Hubungkan tx Bluetooth
  7. Buka app JWS Bluetooth klik setting dan kirim pengaturan
  8. Selesai.

antisipasi masalah:

  1. Data EEPROM sebelumnya mungkin berpengaruh pada settingan, set dengan apk via bluetooth atau hapus dahulu dengan examples->EEPROM->eeprom_clear.
  2. #define modeDemo 1 untuk test / 0 untuk running
  3. Pastikan daya mencukupi (kalo tidak punya 5V > 5A hubungkan saja GND DMD ke GND Arduino tapi tidak Vcc +5V-nya)

nb: V1.0 -> Versi2.0

  1. Karakter informasi yang akan ditampilkan langsung diambil dari I2CEEPROM
  2. Penambahan pengaturan sudut-sudut waktu sholat.
  3. Penambahan efek grafis

catatan:

  • koordinat dalam mode derajat-menit –±dd° mm’–(menit = pecahan derajat*60/100)
    contohnya :
    lintang -6.78 -> -6-46′
    bujur 110.38 -> 110-22′
  • jika koordinat sudah sesuai, namun masih ada perbedaan waktu dengan jadwal yang diinginkan (mis: jadwal depag), maka geser bujur (ditambah/dikurang)
  • Jika sebagian jadwal shalat tidak akurat, maka geser derajat masing-masing jadwal shalat yang belum akurat
  • JWS ini belum menggunakan ikhtiyath, untuk mengantisipasinya bisa dengan menggeser derajat-menit bujur ±2 menit)

Hasil percobaan

by: Priadi Amfi

Hasil percobaan

by: Catur cahya

Hasil percobaan

by: Ochoy Thea