Ps2KeyboardHost  1.0.1
Allows you to read from one or more PS2-style keyboards on an Arduino.
ps2_SimpleDiagnostics.h
Go to the documentation of this file.
1 /*
2 Copyright (C) 2017 Steve Benz <s8878992@hotmail.com>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8 
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 USA
18 */
19 #pragma once
20 #include <stdint.h>
21 #include "ps2_Keyboard.h"
22 #include "ps2_KeyboardOutput.h"
23 #include <util/atomic.h>
24 
25 namespace ps2 {
29  enum class DiagnosticsLedBlink {
34  heartbeat,
35 
39 
41  toggleHigh,
42 
44  toggleLow
45  };
46 
113  template <uint16_t Size = 60, uint16_t LastErrorSize = 30>
115  {
116  byte data[Size];
117  byte lastError[LastErrorSize];
118  uint16_t bytesInLastError = 0;
119  int index = -1;
120  uint16_t failureCodes = 0;
121  uint32_t millisAtLastRecording = 0;
122 
123  enum class Ps2Code : uint8_t {
124  packetDidNotStartWithZero = 0,
125  parityError = 1,
126  packetDidNotEndWithOne = 2,
127  packetIncomplete = 3,
128  sendFrameError = 4,
129  bufferOverflow = 5,
130  incorrectResponse = 6,
131  noResponse = 7,
132  noTranslationForKey = 8,
133  startupFailure = 9,
134  _firstUnusedError = 10,
135 
136  sentByte = 16,
137  receivedByte = 17,
138  pause = 18, // Data is one byte, milliseconds+4/8 (0 to 2.043sec)
139  clockLineGlitch = 19, // data is # of bits received
140  reserved2 = 20,
141  reserved3 = 21,
142  // Reserve a few so that more info-level events can come in without jacking up
143  // any existing readers.
144  _firstUnusedInfo = 22,
145  };
146 
147  void recordFailure(uint8_t code) {
148  if (code < 16) {
149  ATOMIC_BLOCK(ATOMIC_FORCEON) {
150  this->failureCodes |= 1 << code;
151  }
152 
153  uint16_t numBytesCopied = 0;
154  uint16_t i = index < 0 ? -1 - index : index;
155  i = (i == 0 ? Size : i) - 1;
156  while (index >= 0 || i != Size - 1)
157  {
158  byte bytesInWord = 1 + (this->data[i] & 0x3);
159  if (numBytesCopied + bytesInWord > LastErrorSize) {
160  break;
161  }
162 
163  while (bytesInWord > 0) {
164  this->lastError[numBytesCopied] = this->data[i];
165  i = (i == 0 ? Size : i) - 1;
166  ++numBytesCopied;
167  --bytesInWord;
168  }
169  }
170  bytesInLastError = numBytesCopied;
171  }
172  }
173 
174  void pushByte(byte b)
175  {
176  ATOMIC_BLOCK(ATOMIC_FORCEON) {
177  if (index < 0) {
178  data[-1 - index] = b;
179  index = (index == -Size) ? 0 : index - 1;
180  }
181  else {
182  data[index] = b;
183  index = (index == Size - 1) ? 0 : index + 1;
184  }
185  }
186  }
187 
188  void pushRaw(byte code) {
189  pushByte(code << 2);
190  this->recordFailure(code);
191  }
192  void pushRaw(byte code, byte extraData1) {
193  pushByte(extraData1);
194  pushByte((code << 2) | 1);
195  this->recordFailure(code);
196  }
197  void pushRaw(byte code, byte extraData1, byte extraData2) {
198  pushByte(extraData2);
199  pushByte(extraData1);
200  pushByte((code << 2) | 2);
201  this->recordFailure(code);
202  }
203 
204  void recordPause()
205  {
206  unsigned long millisNow = millis();
207  unsigned long timeDelta = millisNow - millisAtLastRecording;
208  if (timeDelta >= 4 && timeDelta < 2044) {
209  pushRaw((byte)Ps2Code::pause, (byte)((timeDelta + 4) >> 3));
210  millisAtLastRecording = millisNow;
211  }
212  else if (timeDelta >= 2044) {
213  unsigned long lowResDelay = (timeDelta + 32) >> 6;
214  if (lowResDelay > 0xffff) {
215  lowResDelay = 0xffff;
216  }
217  pushRaw((byte)Ps2Code::pause, (byte)(lowResDelay >>8), (byte)(lowResDelay & 0xff));
218  millisAtLastRecording = millisNow;
219  }
220  // Else it's too short to make note of
221  }
222 
223  protected:
224  static const uint8_t firstUnusedFailureCode = (uint8_t)Ps2Code::_firstUnusedError;
225  static const uint8_t firstUnusedInfoCode = (uint8_t)Ps2Code::_firstUnusedInfo;
226 
227  template <typename E>
228  void push(E code) {
229  recordPause();
230  pushRaw((byte)code);
231  }
232  template <typename E1,typename E2>
233  void push(E1 code, E2 extraData1) {
234  recordPause();
235  pushRaw((byte)code, (uint8_t)extraData1);
236  }
237  template <typename E1, typename E2, typename E3>
238  void push(E1 code, E2 extraData1, E3 extraData2) {
239  recordPause();
240  pushRaw((byte)code, (byte)extraData1, (byte)extraData2);
241  }
242 
243  public:
247  template <typename Target>
248  void sendReport(Target &printTo) {
249  // This report isn't the least bit human-readable. While developing this software, it became
250  // clear to me that if you have an opportunity to write code in either the Arduino or on a PC,
251  // you choose the PC every time because the development experience is so much better and you
252  // can have richer output. I developed something to read this sequence, but it's not something
253  // I'm comfortable sharing, because it's a Windows-only app, and the quality isn't really ready
254  // for sharing. Rather than invest in that any more, I feel that it should be rewritten as a
255  // JavaScript web page on the wiki or someplace like that.
256  printTo.print("{");
257  printTo.print(this->failureCodes, 16);
258  printTo.print(":");
259 
260  // This content ends up getting reversed
261  for (int i = bytesInLastError-1; i >= 0; --i) {
262  printTo.print(this->lastError[i] >> 4, 16);
263  printTo.print(this->lastError[i] & 0xf, 16);
264  }
265  printTo.print("|");
266 
267  if (this->index < 0)
268  {
269  for (int i = 0; i < -1 - this->index; ++i) {
270  // Can't just do print(data,16), as it'll get truncated if it's < 16.
271  printTo.print(this->data[i] >> 4, 16);
272  printTo.print(this->data[i] & 0xf, 16);
273  }
274  }
275  else {
276  for (int i = this->index; i < Size + this->index; ++i) {
277  printTo.print(this->data[i % Size] >> 4, 16);
278  printTo.print(this->data[i % Size] & 0xf, 16);
279  }
280  }
281  printTo.print("}");
282  }
283 
285  bool anyErrors() const { return this->failureCodes != 0; }
286 
288  void reset() {
289  this->failureCodes = 0;
290  this->index = -1;
291  this->bytesInLastError = 0;
292  }
293 
299  template <uint8_t DiagnosticLedPin = LED_BUILTIN, DiagnosticsLedBlink LedBehavior = DiagnosticsLedBlink::blinkOnError>
301  bool value;
302  switch (LedBehavior) {
304  value = (millis() & (failureCodes != 0 ? 128 : 1024));
305  break;
307  value = (failureCodes == 0 || (millis() & 128));
308  break;
310  value = (failureCodes != 0);
311  break;
313  value = (failureCodes == 0);
314  break;
315  }
316  digitalWrite(DiagnosticLedPin, value ? HIGH : LOW);
317  }
318 
319  void packetDidNotStartWithZero() { this->push(Ps2Code::packetDidNotStartWithZero); }
320  void parityError() { this->push(Ps2Code::parityError); }
321  void packetDidNotEndWithOne() { this->push(Ps2Code::packetDidNotEndWithOne); }
322  void packetIncomplete() { this->push(Ps2Code::packetIncomplete); }
323  void sendFrameError() { this->push(Ps2Code::sendFrameError); }
324  void bufferOverflow() { this->push(Ps2Code::bufferOverflow); }
325  void incorrectResponse(KeyboardOutput scanCode, KeyboardOutput expectedScanCode) {
326  this->push(Ps2Code::incorrectResponse, scanCode, expectedScanCode);
327  }
328  void noResponse(KeyboardOutput expectedScanCode) {
329  this->push(Ps2Code::noResponse, expectedScanCode);
330  }
331  void noTranslationForKey(bool isExtended, KeyboardOutput code) {
332  this->push(Ps2Code::noTranslationForKey, isExtended, code);
333  }
334  void startupFailure() { this->push(Ps2Code::startupFailure); }
335  void clockLineGlitch(uint8_t numBitsSent) {
336  this->push(Ps2Code::clockLineGlitch, numBitsSent);
337  }
338 
339  void sentByte(byte b) { this->push(Ps2Code::sentByte, b); }
340  void receivedByte(byte b) { this->push(Ps2Code::receivedByte, b); }
341  };
342 }
void push(E1 code, E2 extraData1, E3 extraData2)
Definition: ps2_SimpleDiagnostics.h:238
DiagnosticsLedBlink
Used with SimpleDiagnostics to control the behavior of a pin that will signal the device&#39;s user that ...
Definition: ps2_SimpleDiagnostics.h:29
void sendFrameError()
Definition: ps2_SimpleDiagnostics.h:323
Definition: ps2_AnsiTranslator.h:24
void noTranslationForKey(bool isExtended, KeyboardOutput code)
Definition: ps2_SimpleDiagnostics.h:331
void incorrectResponse(KeyboardOutput scanCode, KeyboardOutput expectedScanCode)
Definition: ps2_SimpleDiagnostics.h:325
void push(E1 code, E2 extraData1)
Definition: ps2_SimpleDiagnostics.h:233
void push(E code)
Definition: ps2_SimpleDiagnostics.h:228
void clockLineGlitch(uint8_t numBitsSent)
Definition: ps2_SimpleDiagnostics.h:335
void sentByte(byte b)
Definition: ps2_SimpleDiagnostics.h:339
KeyboardOutput
Byte-codes sent back from the Ps2 keyboard to the host.
Definition: ps2_KeyboardOutput.h:31
void bufferOverflow()
Definition: ps2_SimpleDiagnostics.h:324
void sendReport(Target &printTo)
Dumps all event data to a print-based class.
Definition: ps2_SimpleDiagnostics.h:248
void receivedByte(byte b)
Definition: ps2_SimpleDiagnostics.h:340
void noResponse(KeyboardOutput expectedScanCode)
Definition: ps2_SimpleDiagnostics.h:328
void parityError()
Definition: ps2_SimpleDiagnostics.h:320
A basic recorder for events coming from the PS2 keyboard class library.
Definition: ps2_SimpleDiagnostics.h:114
void startupFailure()
Definition: ps2_SimpleDiagnostics.h:334
void packetDidNotEndWithOne()
Definition: ps2_SimpleDiagnostics.h:321
void packetIncomplete()
Definition: ps2_SimpleDiagnostics.h:322
void reset()
Clears all recorded data.
Definition: ps2_SimpleDiagnostics.h:288
void packetDidNotStartWithZero()
Definition: ps2_SimpleDiagnostics.h:319
bool anyErrors() const
Returns true if any errors have been recorded since the last call to reset.
Definition: ps2_SimpleDiagnostics.h:285
void setLedIndicator()
Enables you to have a blinking indicator when an error happens.
Definition: ps2_SimpleDiagnostics.h:300