435 lines
10 KiB
C++
435 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2011 Cliff L. Biffle, all rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include "Chatpad.h"
|
|
#include "HardwareSerial.h"
|
|
#include <Arduino.h>
|
|
|
|
#include <avr/pgmspace.h>
|
|
|
|
// Masks for modifier bits
|
|
static const byte kShiftMask = (1 << 0);
|
|
static const byte kGreenSquareMask = (1 << 1);
|
|
static const byte kOrangeCircleMask = (1 << 2);
|
|
static const byte kPeopleMask = (1 << 3);
|
|
|
|
// Voodoo protocol messages
|
|
static const byte kInitMessage[] = { 0x87, 0x02, 0x8C, 0x1F, 0xCC };
|
|
static const byte kAwakeMessage[] = { 0x87, 0x02, 0x8C, 0x1B, 0xD0 };
|
|
|
|
void Chatpad::init(HardwareSerial &serial, Chatpad::callback_t callback) {
|
|
_serial = &serial;
|
|
_callback = callback;
|
|
_last_modifiers = 0;
|
|
_last_key0 = 0;
|
|
_last_key1 = 0;
|
|
_last_ping = 0;
|
|
_serial->begin(19200);
|
|
_serial->write(kInitMessage, sizeof(kInitMessage));
|
|
}
|
|
|
|
void Chatpad::poll() {
|
|
|
|
|
|
// Only act if a full message is available.
|
|
if (_serial->available() >= 8) {
|
|
for (int i = 0; i < 8; i++) {
|
|
_buffer[i] = _serial->read();
|
|
}
|
|
|
|
//Serial.println("no pool..");
|
|
|
|
// We expect "status report" packets beginning with 0xA5, but don't know
|
|
// what to do with them -- so we silently discard them.
|
|
if (_buffer[0] == 0xA5) return;
|
|
|
|
// We *do not* expect other types of packets. If we find one, complain
|
|
// to the user.
|
|
if (_buffer[0] != 0xB4) {
|
|
Serial.print("Unexpected packet type: ");
|
|
Serial.println(_buffer[0], HEX);
|
|
return;
|
|
}
|
|
if (_buffer[1] != 0xC5) {
|
|
Serial.print("Unexpected second byte: ");
|
|
Serial.println(_buffer[1], HEX);
|
|
return;
|
|
}
|
|
|
|
// Check the checksum.
|
|
unsigned char checksum = _buffer[0];
|
|
for (int i = 1; i < 7; i++) checksum += _buffer[i];
|
|
checksum = -checksum;
|
|
if (checksum != _buffer[7]) {
|
|
Serial.println("Checksum failure");
|
|
return;
|
|
}
|
|
|
|
// Packet looks good!
|
|
// Dissect the parts we care about:
|
|
byte modifiers = _buffer[3];
|
|
byte key0 = _buffer[4];
|
|
byte key1 = _buffer[5];
|
|
|
|
// Check for changes in the modifiers.
|
|
byte modifier_changes = modifiers ^ _last_modifiers;
|
|
if (modifier_changes & kShiftMask) {
|
|
dispatch(KeyShift, modifiers & kShiftMask);
|
|
}
|
|
if (modifier_changes & kGreenSquareMask) {
|
|
dispatch(KeyGreenSquare, modifiers & kGreenSquareMask);
|
|
}
|
|
if (modifier_changes & kOrangeCircleMask) {
|
|
dispatch(KeyOrangeCircle, modifiers & kOrangeCircleMask);
|
|
}
|
|
if (modifier_changes & kPeopleMask) {
|
|
dispatch(KeyPeople, modifiers & kPeopleMask);
|
|
}
|
|
_last_modifiers = modifiers;
|
|
|
|
// Check for changes in the other keys
|
|
if (key0 && key0 != _last_key0 && key0 != _last_key1) {
|
|
dispatch(key0, Down);
|
|
}
|
|
if (key1 && key1 != _last_key0 && key1 != _last_key1) {
|
|
dispatch(key1, Down);
|
|
}
|
|
if (_last_key0 && _last_key0 != key0 && _last_key0 != key1) {
|
|
dispatch(_last_key0, Up);
|
|
}
|
|
if (_last_key1 && _last_key1 != key0 && _last_key1 != key1) {
|
|
dispatch(_last_key1, Up);
|
|
}
|
|
_last_key0 = key0;
|
|
_last_key1 = key1;
|
|
}
|
|
|
|
uint32_t time = millis();
|
|
if (time - _last_ping > 1000) {
|
|
_last_ping = time;
|
|
_serial->write(kAwakeMessage, sizeof(kAwakeMessage));
|
|
}
|
|
}
|
|
|
|
bool Chatpad::isShiftDown() const {
|
|
return _last_modifiers & kShiftMask;
|
|
}
|
|
|
|
bool Chatpad::isGreenSquareDown() const {
|
|
return _last_modifiers & kGreenSquareMask;
|
|
}
|
|
|
|
bool Chatpad::isOrangeCircleDown() const {
|
|
return _last_modifiers & kOrangeCircleMask;
|
|
}
|
|
|
|
bool Chatpad::isPeopleDown() const {
|
|
return _last_modifiers & kPeopleMask;
|
|
}
|
|
|
|
void Chatpad::dispatch(uint8_t code, int is_down) {
|
|
_callback(*this, (keycode_t) code, is_down? Down : Up);
|
|
}
|
|
|
|
/**************
|
|
* Translation
|
|
*/
|
|
|
|
// These tables have been compacted and must be accessed using this formula:
|
|
// index = (((keycode & 0xF0) - 0x10) >> 1) | ((keycode & 0x0F) - 1)
|
|
static const char kAsciiTable[] PROGMEM = {
|
|
'7', /* 11 Key7 */
|
|
'6', /* 12 Key6 */
|
|
'5', /* 13 Key5 */
|
|
'4', /* 14 Key4 */
|
|
'3', /* 15 Key3 */
|
|
'2', /* 16 Key2 */
|
|
'1', /* 17 Key1 */
|
|
0, /* 18 Unused */
|
|
|
|
'u', /* 21 KeyU */
|
|
'y', /* 22 KeyY */
|
|
't', /* 23 KeyT */
|
|
'r', /* 24 KeyR */
|
|
'e', /* 25 KeyE */
|
|
'w', /* 26 KeyW */
|
|
'q', /* 27 KeyQ */
|
|
0, /* 28 Unused */
|
|
|
|
'j', /* 31 KeyJ */
|
|
'h', /* 32 KeyH */
|
|
'g', /* 33 KeyG */
|
|
'f', /* 34 KeyF */
|
|
'd', /* 35 KeyD */
|
|
's', /* 36 KeyS */
|
|
'a', /* 37 KeyA */
|
|
0, /* 38 Unused */
|
|
|
|
'n', /* 41 KeyN */
|
|
'b', /* 42 KeyB */
|
|
'v', /* 43 KeyV */
|
|
'c', /* 44 KeyC */
|
|
'x', /* 45 KeyX */
|
|
'z', /* 46 KeyZ */
|
|
0, /* 47 Unused */
|
|
0, /* 48 Unused */
|
|
|
|
'\t', /* 51 KeyRight */
|
|
'm', /* 52 KeyM */
|
|
'.', /* 53 KeyPeriod */
|
|
' ', /* 54 KeySpace */
|
|
'\v', /* 55 KeyLeft */
|
|
0, /* 56 Unused */
|
|
0, /* 57 Unused */
|
|
0, /* 58 Unused */
|
|
|
|
0, /* 61 Unused */
|
|
',', /* 62 KeyComma */
|
|
'\n', /* 63 KeyEnter */
|
|
'p', /* 64 KeyP */
|
|
'0', /* 65 Key0 */
|
|
'9', /* 66 Key9 */
|
|
'8', /* 67 Key8 */
|
|
0, /* 68 Unused */
|
|
|
|
'\b', /* 71 KeyBackspace */
|
|
'l', /* 72 KeyL */
|
|
0, /* 73 Unused */
|
|
0, /* 74 Unused */
|
|
'o', /* 75 KeyO */
|
|
'i', /* 76 KeyI */
|
|
'k', /* 77 KeyK */
|
|
0, /* 78 Unused */
|
|
};
|
|
|
|
static const char kAsciiTable_Shifted[] PROGMEM = {
|
|
'&', /* 11 Key7 */
|
|
'^', /* 12 Key6 */
|
|
'%', /* 13 Key5 */
|
|
'$', /* 14 Key4 */
|
|
'#', /* 15 Key3 */
|
|
'@', /* 16 Key2 */
|
|
'!', /* 17 Key1 */
|
|
0, /* 18 Unused */
|
|
|
|
'U', /* 21 KeyU */
|
|
'Y', /* 22 KeyY */
|
|
'T', /* 23 KeyT */
|
|
'R', /* 24 KeyR */
|
|
'E', /* 25 KeyE */
|
|
'W', /* 26 KeyW */
|
|
'Q', /* 27 KeyQ */
|
|
0, /* 28 Unused */
|
|
|
|
'J', /* 31 KeyJ */
|
|
'H', /* 32 KeyH */
|
|
'G', /* 33 KeyG */
|
|
'F', /* 34 KeyF */
|
|
'D', /* 35 KeyD */
|
|
'S', /* 36 KeyS */
|
|
'A', /* 37 KeyA */
|
|
0, /* 38 Unused */
|
|
|
|
'N', /* 41 KeyN */
|
|
'B', /* 42 KeyB */
|
|
'V', /* 43 KeyV */
|
|
'C', /* 44 KeyC */
|
|
'X', /* 45 KeyX */
|
|
'Z', /* 46 KeyZ */
|
|
0, /* 47 Unused */
|
|
0, /* 48 Unused */
|
|
|
|
'\t', /* 51 KeyRight */
|
|
'M', /* 52 KeyM */
|
|
'>', /* 53 KeyPeriod */
|
|
' ', /* 54 KeySpace */
|
|
'\v', /* 55 KeyLeft */
|
|
0, /* 56 Unused */
|
|
0, /* 57 Unused */
|
|
0, /* 58 Unused */
|
|
|
|
0, /* 61 Unused */
|
|
'<', /* 62 KeyComma */
|
|
'\n', /* 63 KeyEnter */
|
|
'P', /* 64 KeyP */
|
|
')', /* 65 Key0 */
|
|
'(', /* 66 Key9 */
|
|
'*', /* 67 Key8 */
|
|
0, /* 68 Unused */
|
|
|
|
'\b', /* 71 KeyBackspace */
|
|
'L', /* 72 KeyL */
|
|
0, /* 73 Unused */
|
|
0, /* 74 Unused */
|
|
'O', /* 75 KeyO */
|
|
'I', /* 76 KeyI */
|
|
'K', /* 77 KeyK */
|
|
0, /* 78 Unused */
|
|
};
|
|
|
|
static const char kAsciiTable_Orange[] PROGMEM = {
|
|
0, /* 11 Key7 */
|
|
0, /* 12 Key6 */
|
|
0, /* 13 Key5 */
|
|
0, /* 14 Key4 */
|
|
0, /* 15 Key3 */
|
|
0, /* 16 Key2 */
|
|
0, /* 17 Key1 */
|
|
0, /* 18 Unused */
|
|
|
|
0, /* 21 KeyU */
|
|
0, /* 22 KeyY */
|
|
0, /* 23 KeyT */
|
|
'$', /* 24 KeyR */
|
|
0, /* 25 KeyE */
|
|
0, /* 26 KeyW */
|
|
0, /* 27 KeyQ */
|
|
0, /* 28 Unused */
|
|
|
|
'"', /* 31 KeyJ */
|
|
'\\', /* 32 KeyH */
|
|
0, /* 33 KeyG */
|
|
0, /* 34 KeyF */
|
|
0, /* 35 KeyD */
|
|
0, /* 36 KeyS */
|
|
0, /* 37 KeyA */
|
|
0, /* 38 Unused */
|
|
|
|
0, /* 41 KeyN */
|
|
'+', /* 42 KeyB */
|
|
'-', /* 43 KeyV */
|
|
0, /* 44 KeyC */
|
|
0, /* 45 KeyX */
|
|
0, /* 46 KeyZ */
|
|
0, /* 47 Unused */
|
|
0, /* 48 Unused */
|
|
|
|
'\t', /* 51 KeyRight */
|
|
0, /* 52 KeyM */
|
|
0, /* 53 KeyPeriod */
|
|
' ', /* 54 KeySpace */
|
|
'\v', /* 55 KeyLeft */
|
|
0, /* 56 Unused */
|
|
0, /* 57 Unused */
|
|
0, /* 58 Unused */
|
|
|
|
0, /* 61 Unused */
|
|
';', /* 62 KeyComma */
|
|
'\n', /* 63 KeyEnter */
|
|
'=', /* 64 KeyP */
|
|
0, /* 65 Key0 */
|
|
0, /* 66 Key9 */
|
|
0, /* 67 Key8 */
|
|
0, /* 68 Unused */
|
|
|
|
'\b', /* 71 KeyBackspace */
|
|
0, /* 72 KeyL */
|
|
0, /* 73 Unused */
|
|
0, /* 74 Unused */
|
|
0, /* 75 KeyO */
|
|
0, /* 76 KeyI */
|
|
0, /* 77 KeyK */
|
|
0, /* 78 Unused */
|
|
};
|
|
|
|
static const char kAsciiTable_Green[] PROGMEM = {
|
|
0, /* 11 Key7 */
|
|
0, /* 12 Key6 */
|
|
0, /* 13 Key5 */
|
|
0, /* 14 Key4 */
|
|
0, /* 15 Key3 */
|
|
0, /* 16 Key2 */
|
|
0, /* 17 Key1 */
|
|
0, /* 18 Unused */
|
|
|
|
'&', /* 21 KeyU */
|
|
'^', /* 22 KeyY */
|
|
'%', /* 23 KeyT */
|
|
'#', /* 24 KeyR */
|
|
0, /* 25 KeyE */
|
|
'@', /* 26 KeyW */
|
|
'!', /* 27 KeyQ */
|
|
0, /* 28 Unused */
|
|
|
|
'\'', /* 31 KeyJ */
|
|
'/', /* 32 KeyH */
|
|
0, /* 33 KeyG */
|
|
'}', /* 34 KeyF */
|
|
'{', /* 35 KeyD */
|
|
0, /* 36 KeyS */
|
|
'~', /* 37 KeyA */
|
|
0, /* 38 Unused */
|
|
|
|
'<', /* 41 KeyN */
|
|
'|', /* 42 KeyB */
|
|
'_', /* 43 KeyV */
|
|
0, /* 44 KeyC */
|
|
0, /* 45 KeyX */
|
|
'`', /* 46 KeyZ */
|
|
0, /* 47 Unused */
|
|
0, /* 48 Unused */
|
|
|
|
'\t', /* 51 KeyRight */
|
|
'>', /* 52 KeyM */
|
|
'?', /* 53 KeyPeriod */
|
|
' ', /* 54 KeySpace */
|
|
'\v', /* 55 KeyLeft */
|
|
0, /* 56 Unused */
|
|
0, /* 57 Unused */
|
|
0, /* 58 Unused */
|
|
|
|
0, /* 61 Unused */
|
|
':', /* 62 KeyComma */
|
|
'\n', /* 63 KeyEnter */
|
|
')', /* 64 KeyP */
|
|
0, /* 65 Key0 */
|
|
0, /* 66 Key9 */
|
|
0, /* 67 Key8 */
|
|
0, /* 68 Unused */
|
|
|
|
'\b', /* 71 KeyBackspace */
|
|
']', /* 72 KeyL */
|
|
0, /* 73 Unused */
|
|
0, /* 74 Unused */
|
|
'(', /* 75 KeyO */
|
|
'*', /* 76 KeyI */
|
|
'[', /* 77 KeyK */
|
|
0, /* 78 Unused */
|
|
};
|
|
|
|
char Chatpad::toAscii(keycode_t code) {
|
|
byte index = (((code - 0x11) & 0x70) >> 1) | ((code - 0x11) & 0x7);
|
|
if (index >= sizeof(kAsciiTable)) return 0;
|
|
|
|
if (isShiftDown()) {
|
|
return pgm_read_byte_near(kAsciiTable_Shifted + index);
|
|
} else if (isOrangeCircleDown() || isPeopleDown() ) {
|
|
return pgm_read_byte_near(kAsciiTable_Orange + index);
|
|
} else if (isGreenSquareDown()) {
|
|
return pgm_read_byte_near(kAsciiTable_Green + index);
|
|
} else {
|
|
return pgm_read_byte_near(kAsciiTable + index);
|
|
}
|
|
}
|