Friday, May 22, 2020

ATOM Matrix: Onboard IR LED and Remote Control Emulator

The ATOM Matrix has an onboard IR LED. To toggle the IR LED, use the same code as from the last post, but change the outputPin from 26 to 12:

 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
/*
 * IR-LED-01.ino
 * 
 * By: Mike Klepper
 * Date: 26 April 2020
 * 
 * Demonstrates how to turn the IR LED on and off
 */

#include "M5Atom.h"

int outputPin = 12;

void setup() 
{
  M5.begin(true, false, true);
  delay(20);
  pinMode(outputPin, OUTPUT);
}

void loop() 
{
  digitalWrite(12, HIGH);
  delay(500);
  M5.update();

  digitalWrite(12, LOW);
  delay(500);
  M5.update();
}

When you run this program and point the ATOM Matrix at your webcam, you will notice that the IR LED's output is not bright at all. This will cause problems for our remote control emulator! :-(


IR Codes for a Particular Remote Control

I will be using the IR remote control that came with a Sony Blu-ray player.

Using the IRrecvDumpV2 program from the last blog post, the codes for some of the buttons on that remote control are as follows:

Button Code
On/Off 0xA8B47
Open/Close 0x68B47
Popup Menu 0x94B47
Left Arrow 0xDCB47
Right Arrow 0x3CB47
Up Arrow 0x9CB47
Down Arrow 0x5CB47
Select 0xBCB47
Play 0x58B47
Pause 0x98B47
Stop 0x18B47
Reverse 0xD8B47
Forward 0x38B47
Previous Scene 0xEAB47
Next Scene 0x6AB47


IR Remote Control Emulator

Now combine these codes with the one-button menu system found in a previous blog post, and the result is an IR remote control emulator! The secret sauce is in the IRremoteESP8266 library installed in the last blog post. This library has a IRsend class with format-specific commands, like sendNEC, sendSony, etc., for transmitting IR commands.

  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
/*
 * Sony-IR-Remote-Emulator.ino
 * 
 * By: Mike Klepper
 * Date: 26 April 2020
 * 
 * Emulates a Sony Blu-ray Player IR Remote Control
 * 
 * See post on patriot-geek.blogspot.com
 * for details
 */


#include "M5Atom.h"
#include <IRremoteESP8266.h>
#include <IRsend.h>

// const uint16_t kIrLed = 12;  // Internal IR LED
const uint16_t kIrLed = 26;  // IR Remote Grove Module IR LED

IRsend irsend(kIrLed);  // Set the GPIO to be used to sending the message.

int GRB_COLOR_WHITE = 0xffffff;
int GRB_COLOR_BLACK = 0x000000;
int GRB_COLOR_RED = 0x00ff00;
int GRB_COLOR_ORANGE = 0xa5ff00;
int GRB_COLOR_YELLOW = 0xffff00;
int GRB_COLOR_GREEN = 0xff0000;
int GRB_COLOR_BLUE = 0x0000ff;
int GRB_COLOR_PURPLE = 0x008080;

int colorList[] = {GRB_COLOR_BLACK, GRB_COLOR_WHITE, GRB_COLOR_RED, GRB_COLOR_GREEN};

int empty[25] = 
{
  0,0,0,0,0,
  0,0,0,0,0,
  0,0,0,0,0,
  0,0,0,0,0,
  0,0,0,0,0
};

int onOff[25] = 
{
  0,2,3,2,0,
  2,0,3,0,2,
  2,0,3,0,2,
  2,0,0,0,2,
  0,2,2,2,0
};

int onOffCode = 0xA8B47;


int openClose[25] = 
{
  0,0,1,0,0,
  0,1,1,1,0,
  1,1,1,1,1,
  0,0,0,0,0,
  1,1,1,1,1
};

int openCloseCode = 0x68B47;


int popUpMenu[25] = 
{
  0,0,1,0,0,
  0,1,0,1,0,
  0,0,0,0,0,
  0,0,1,0,0,
  0,1,0,1,0
};

int popUpMenuCode = 0x94B47;


int leftArrow[25] = 
{
  0,0,1,0,0,
  0,1,0,0,0,
  1,1,1,1,1,
  0,1,0,0,0,
  0,0,1,0,0
};

int leftArrowCode = 0xDCB47;


int rightArrow[25] = 
{
  0,0,1,0,0,
  0,0,0,1,0,
  1,1,1,1,1,
  0,0,0,1,0,
  0,0,1,0,0
};

int rightArrowCode = 0x3CB47;


int upArrow[25] = 
{
  0,0,1,0,0,
  0,1,1,1,0,
  1,0,1,0,1,
  0,0,1,0,0,
  0,0,1,0,0
};

int upArrowCode = 0x9CB47;


int downArrow[25] = 
{
  0,0,1,0,0,
  0,0,1,0,0,
  1,0,1,0,1,
  0,1,1,1,0,
  0,0,1,0,0
};

int downArrowCode = 0x5CB47;


int select[25] = 
{
  0,0,3,0,0,
  0,3,3,3,0,
  3,3,3,3,3,
  0,3,3,3,0,
  0,0,3,0,0
};

int selectCode = 0xBCB47;


int playBtn[25] = 
{
  0,0,3,0,0,
  0,0,3,3,0,
  0,0,3,3,3,
  0,0,3,3,0,
  0,0,3,0,0
};

int playBtnCode = 0x58B47;


int pauseBtn[25] = 
{
  0,0,0,0,0,
  0,2,0,2,0,
  0,2,0,2,0,
  0,2,0,2,0,
  0,0,0,0,0
};

int pauseBtnCode = 0x98B47;


int stopBtn[25] = 
{
  0,0,0,0,0,
  0,2,2,2,0,
  0,2,2,2,0,
  0,2,2,2,0,
  0,0,0,0,0
};

int stopBtnCode = 0x18B47;


int reverseBtn[25] = 
{
  0,0,0,0,0,
  0,3,0,0,3,
  3,0,0,3,0,
  0,3,0,0,3,
  0,0,0,0,0
};

int reverseBtnCode = 0xD8B47;


int forwardBtn[25] = 
{
  0,0,0,0,0,
  3,0,0,3,0,
  0,3,0,0,3,
  3,0,0,3,0,
  0,0,0,0,0
};

int forwardBtnCode = 0x38B47;


int prevScene[25] = 
{
  1,0,0,0,1,
  1,0,0,1,1,
  1,0,1,1,1,
  1,0,0,1,1,
  1,0,0,0,1
};

int prevSceneCode = 0xEAB47;


int nextScene[25] = 
{
  1,0,0,0,1,
  1,1,0,0,1,
  1,1,1,0,1,
  1,1,0,0,1,
  1,0,0,0,1
};

int nextSceneCode = 0x6AB47;

const int numChoices = 15;
int *iconList[numChoices] = { onOff, openClose, popUpMenu, leftArrow, rightArrow, upArrow, downArrow, select, playBtn, pauseBtn, stopBtn, reverseBtn, forwardBtn, prevScene, nextScene };
int codeList[numChoices] = { onOffCode, openCloseCode, popUpMenuCode, leftArrowCode, rightArrowCode, upArrowCode, downArrowCode, selectCode, playBtnCode, pauseBtnCode, stopBtnCode, reverseBtnCode, forwardBtnCode, prevSceneCode, nextSceneCode };

int currentState = 0;
bool isAsleep = false;
bool wasLongPressed = false;
bool commandExecuted = false;


void setup() 
{
  M5.begin(true, false, true);
  delay(20);
  
  irsend.begin();
  
  drawArray2(iconList[currentState], colorList, 10);
}

void loop() 
{
  if(M5.Btn.wasPressed())
  {
    // Display icon
    drawArray2(iconList[currentState], colorList, 10);

    commandExecuted = false;
  }
  
  if(M5.Btn.wasReleased())
  {
    Serial.print("isAsleep = ");
    Serial.println(isAsleep);
    Serial.print("wasLongPressed = ");
    Serial.println(wasLongPressed);
    
    if(wasLongPressed == false)
    {
      if(isAsleep == false)
      {
        // Advance to next state in the cycle
        currentState = (currentState + 1) % numChoices;
        
      }
      isAsleep = false;
      commandExecuted = false;
      Serial.println(currentState);
  
      // Display icon
      drawArray2(iconList[currentState], colorList, 10);
    }

    wasLongPressed = false;
  }
  
  // Long press
  if(M5.Btn.pressedFor(1000))
  {
    wasLongPressed = true;
    
    // Display icon as active
    drawArray2(iconList[currentState], colorList, 25);
    
    // Perform corresponding action
    if(commandExecuted == false)
    {
      irsend.sendSony(codeList[currentState], 20, 2);
      commandExecuted = true;
      Serial.println("pressedFor");
    }
  }
  
  // Clear display after period of inactivity
  if(M5.Btn.releasedFor(2000))
  {
    M5.dis.clear();
    isAsleep = true;
    commandExecuted = false;
  }

  // Reset to initial state after LONG period of inactivity
  if(M5.Btn.releasedFor(30000))
  {
    currentState = 0;
    M5.dis.clear();
    isAsleep = true;
    commandExecuted = false;
    //Serial.println("Reset currentState to 0");
  }
  
  delay(50);
  M5.update();
}

void drawArray2(int arr[], int colors[], int brightness)
{
  M5.dis.clear();
  M5.dis.setBrightness(brightness);
  for(int i = 0; i < 25; i++)
  {
      M5.dis.drawpix(i, colors[arr[i]]);
  }
}

The code is almost identical to the one-button menu application, and this line-by-line commentary will focus only on the differences:

Line Comment
18 - 19 Select the desired IR LED - the onboard IR LED won't work beyond moderate distances
21 Create an IRsend object for transmitting IR codes
32 The colorList is longer than usual since we'll be using multi-color icons!
34 - 218 Icons and the IR codes
222 Number of choices in our menu system
223 - 224 List of icons and IR codes
237 Initialize the irsend object created in line 21
239 Draw the current icon using the colorList and at brightness level 10
289 Send the IR code in the Sony format, padding to 20 bits and repeating twice
272 Draw icon at brightness level 10
284 When button is long-pressed, draw icon at brightness level 25
317 - 325 New, improved, drawArray2 function that lets us control brightness


About the Onboard IR...

As mentioned above, the onboard IR is fairly dim, and it isn't sufficiently bright to control my Blu-ray player at a distance of 6 feet. For now, we will leave the Grove IR Remote module plugged in. A workaround will be implemented in a future post!

Click here to go to the table of contents for this series.

No comments:

Post a Comment