813 lines
23 KiB
C++
813 lines
23 KiB
C++
/**
|
|
* \file cimditprofile.h
|
|
* \brief Declaration of the CimditProfile class
|
|
* \author GrumpyDeveloper (Sascha Nitsch)
|
|
* \copyright 2022 Sascha Nitsch
|
|
* Licensed under MIT license
|
|
*
|
|
*/
|
|
#include "defines.h"
|
|
|
|
// system includes
|
|
#include <Arduino.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#ifndef EXTERNAL_EEPROM
|
|
#include <EEPROM.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_DISPLAY
|
|
#include "cimditssd1306.h"
|
|
#endif
|
|
|
|
// own includes
|
|
#include "cimditprofile.h"
|
|
|
|
extern CimditSSD1306 display;
|
|
extern CimditHAL hal;
|
|
|
|
/*
|
|
structure of profile in eeprom
|
|
uint16_t total size (including length bytes)
|
|
uint8_t number of profiles
|
|
for each profile:
|
|
uint16_t length length of profile in bytes (including length bytes)
|
|
char profile[] 0-terminated profile name
|
|
uint8_t custom image (0/1)
|
|
if custom image
|
|
uint8_t[512] image data
|
|
uint8_t button_mapping number
|
|
repeat for each mapped button
|
|
uint8_t button number
|
|
<mapping enum> uint8_t mapping type
|
|
int8_t value depending on mapping
|
|
uint8_t axis mapping number
|
|
repeated for each mapped axis
|
|
uint8_t axis number
|
|
<mapping enum> uint8_t mapping type
|
|
int8_t value depending on mapping
|
|
uint8_t rotary mapping number
|
|
repeated for each rotary encoder
|
|
uint8_t encoder number (note, each encoder has 2 buttons: counter-clockwise and clockwise)
|
|
<mapping enum> uint8_t mapping type
|
|
int8_t value depending on mapping
|
|
uint8_t number of macro sequences
|
|
repeated for every macro sequence
|
|
const char[] macro sequence, 0-terminated
|
|
*/
|
|
|
|
CimditProfile::CimditProfile() {
|
|
m_numProfiles = 0;
|
|
for (uint8_t i = 0; i < NUMBER_OF_PROFILES; ++i) {
|
|
m_profiles[i][0] = 0;
|
|
m_mappedRotary[i].m_number = -1;
|
|
}
|
|
m_numMappedButtons = 0;
|
|
m_numMappedAxis = 0;
|
|
m_numMappedRotary = 0;
|
|
m_activeProfile = 0;
|
|
m_selectingProfile = 0;
|
|
m_mouseMoveX = 0;
|
|
m_mouseMoveY = 0;
|
|
m_eepromPosition = 0;
|
|
m_stateTimeout = 0;
|
|
m_numMacros = 0;
|
|
m_macroStart = nullptr;
|
|
m_nextBarUpdate = 0;
|
|
m_dotX = 0;
|
|
m_dotY = 0;
|
|
m_relMouseX = 0;
|
|
m_relMouseY = 0;
|
|
m_displayState = DISPLAY_BLANK;
|
|
m_userTimeout = 0;
|
|
m_stateStarted = 0;
|
|
m_customImage = 0;
|
|
memset(m_userString, 0, sizeof(m_userString));
|
|
}
|
|
|
|
uint8_t CimditProfile::nextUInt8() {
|
|
uint8_t ret;
|
|
#ifdef EXTERNAL_EEPROM
|
|
Wire.beginTransmission(EXTERNAL_EEPROM);
|
|
Wire.write((uint8_t)(m_eepromPosition >> 8)); // MSB
|
|
Wire.write((uint8_t)(m_eepromPosition & 0xFF)); // LSB
|
|
Wire.endTransmission();
|
|
Wire.requestFrom(EXTERNAL_EEPROM, 1);
|
|
ret = Wire.read();
|
|
++m_eepromPosition;
|
|
return ret;
|
|
#else
|
|
return EEPROM.read(m_eepromPosition++);
|
|
#endif
|
|
}
|
|
|
|
uint16_t CimditProfile::nextUInt16() {
|
|
return ((uint16_t)(nextUInt8()) << 8) + nextUInt8();
|
|
}
|
|
|
|
void CimditProfile::nextString(unsigned char* destination) {
|
|
uint8_t next = nextUInt8();
|
|
while (next != 0) {
|
|
*destination = next;
|
|
next = nextUInt8();
|
|
++destination;
|
|
}
|
|
*destination = 0;
|
|
}
|
|
|
|
void CimditProfile::begin() {
|
|
initProfiles();
|
|
}
|
|
|
|
void CimditProfile::initProfiles() {
|
|
m_eepromPosition = 2;
|
|
// number of profiles
|
|
m_numProfiles = nextUInt8();
|
|
if (m_numProfiles > NUMBER_OF_PROFILES) m_numProfiles = 0;
|
|
for (uint8_t i = 0; i < m_numProfiles; ++i) {
|
|
uint16_t position = m_eepromPosition;
|
|
uint16_t size = nextUInt16();
|
|
nextString((unsigned char*)(m_profiles[i]));
|
|
m_eepromPosition = position + size; // we only scan the names here
|
|
}
|
|
if (m_numProfiles>0) {
|
|
load(0);
|
|
}
|
|
}
|
|
|
|
void CimditProfile::load(uint8_t num) {
|
|
m_activeProfile = num;
|
|
m_selectingProfile = num;
|
|
// skip number of profiles
|
|
m_eepromPosition = 3;
|
|
// seek to right profile
|
|
for (uint8_t i = 0; i < num; ++i) {
|
|
// get profile length
|
|
uint16_t size = nextUInt16();
|
|
m_eepromPosition += size - 2;
|
|
}
|
|
// ignore 2 bytes size
|
|
m_eepromPosition += 2;
|
|
// m_eepromPosition points to our profile to parse
|
|
// name is already saved, skip it
|
|
while (nextUInt8() != 0) {
|
|
}
|
|
|
|
// do we have a custom image?
|
|
m_customImage = nextUInt8() > 0 ? m_eepromPosition : 0;
|
|
if (m_customImage) {
|
|
// skip 512 bytes
|
|
m_eepromPosition += 512;
|
|
}
|
|
// mapped buttons
|
|
// number of mapped buttons
|
|
m_numMappedButtons = nextUInt8();
|
|
if (m_numMappedButtons) {
|
|
for (uint8_t i = 0; i < m_numMappedButtons ; ++i) {
|
|
m_mappedButtons[i].m_number = nextUInt8();
|
|
m_mappedButtons[i].m_type = (MappingType)(nextUInt8());
|
|
m_mappedButtons[i].m_value = nextUInt8();
|
|
}
|
|
}
|
|
// end of mapped buttons
|
|
|
|
// mapped axis
|
|
// number of mapped axis
|
|
m_numMappedAxis = nextUInt8();
|
|
if (m_numMappedAxis) {
|
|
for (uint8_t i = 0; i < m_numMappedAxis ; ++i) {
|
|
m_mappedAxis[i].m_number = nextUInt8();
|
|
m_mappedAxis[i].m_type = (MappingType)(nextUInt8());
|
|
m_mappedAxis[i].m_value = nextUInt8();
|
|
}
|
|
}
|
|
// end of mapped axis
|
|
|
|
// mapped rotary
|
|
// number of mapped rotary
|
|
m_numMappedRotary = nextUInt8();
|
|
if (m_numMappedRotary) {
|
|
for (uint8_t i = 0; i < m_numMappedRotary ; ++i) {
|
|
m_mappedRotary[i].m_number = nextUInt8();
|
|
m_mappedRotary[i].m_type = (MappingType)(nextUInt8());
|
|
m_mappedRotary[i].m_value = nextUInt8();
|
|
}
|
|
}
|
|
// end of mapped rotary
|
|
m_mouseMoveX = 0;
|
|
m_mouseMoveY = 0;
|
|
// parse macro
|
|
m_numMacros = nextUInt8();
|
|
if (m_macroStart) {
|
|
free(m_macroStart);
|
|
}
|
|
m_macroStart = reinterpret_cast<uint16_t*>(malloc(m_numMacros * sizeof(uint16_t)));
|
|
for (uint8_t macroNum = 0; macroNum < m_numMacros; ++macroNum) {
|
|
m_macroStart[macroNum] = m_eepromPosition;
|
|
scanOrExecuteMacro(macroNum, false);
|
|
}
|
|
showActiveProfile();
|
|
}
|
|
|
|
void CimditProfile::scanOrExecuteMacro(uint8_t macroNum, bool execute) {
|
|
m_eepromPosition = m_macroStart[macroNum];
|
|
uint8_t value8 = 0;
|
|
uint16_t delayTime = 0;
|
|
MACROCOMMANDS token;
|
|
do {
|
|
token = (MACROCOMMANDS)nextUInt8();
|
|
switch (token) {
|
|
case MACRO_NULL:
|
|
break;
|
|
case MACRO_SPEED: // read in 1 extra numberic value 8 bit
|
|
delayTime = 1000 / nextUInt8();
|
|
break;
|
|
case MACRO_JOY_PRESS: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Gamepad.press(value8);
|
|
Gamepad.write();
|
|
}
|
|
break;
|
|
case MACRO_JOY_RELEASE: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Gamepad.release(value8);
|
|
Gamepad.write();
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_PRESS: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Mouse.press(value8);
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_RELEASE: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Mouse.release(value8);
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_WHEEL: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Mouse.move(0, 0, value8);
|
|
}
|
|
break;
|
|
case MACRO_DELAY: // read in 1 extra numberic value 16 bit
|
|
if (execute) {
|
|
delay(nextUInt16());
|
|
}
|
|
break;
|
|
case MACRO_KEY_PRESS: // one or more keys
|
|
while ((value8 = nextUInt8()) != MACRO_END) {
|
|
if (execute) {
|
|
Keyboard.press((KeyboardKeycode)value8);
|
|
if (delayTime) delay(delayTime);
|
|
}
|
|
}
|
|
break;
|
|
case MACRO_KEY_RELEASE:
|
|
while ((value8 = nextUInt8()) != MACRO_END) {
|
|
if (execute) {
|
|
Keyboard.release((KeyboardKeycode)value8);
|
|
if (delayTime) delay(delayTime);
|
|
}
|
|
}
|
|
break;
|
|
case MACRO_TYPE:
|
|
while ((value8 = nextUInt8()) != MACRO_END) {
|
|
if (execute) {
|
|
Keyboard.write((KeyboardKeycode)value8);
|
|
if (delayTime) delay(delayTime);
|
|
}
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_REL_X: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Mouse.move(value8, 0, 0);
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_REL_Y: // read in 1 extra numberic value 8 bit
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
Mouse.move(0, value8, 0);
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_REL_X_AXIS: { // one or 2 bytes"
|
|
int8_t axis = nextUInt8();
|
|
if (axis != -1) {
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
// find previous mapping
|
|
bool found = false;
|
|
for (uint8_t i = 0; i < m_numMappedAxis; ++i) {
|
|
if (m_mappedAxis[i].m_number == axis) {
|
|
m_mappedAxis[i].m_type = MOUSE_REL_X_AXIS;
|
|
m_mappedAxis[i].m_value = value8;
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
m_mappedAxis[m_numMappedAxis].m_type = MOUSE_REL_X_AXIS;
|
|
m_mappedAxis[m_numMappedAxis].m_number = axis;
|
|
m_mappedAxis[m_numMappedAxis++].m_value = value8;
|
|
}
|
|
}
|
|
} else {
|
|
if (execute) {
|
|
// find axis
|
|
for (uint8_t i = 0; i < m_numMappedAxis; ++i) {
|
|
if (m_mappedAxis[i].m_number == axis) {
|
|
m_mappedAxis[i].m_type = MAPPING_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case MACRO_MOUSE_REL_Y_AXIS: { // one or 2 bytes
|
|
int8_t axis = nextUInt8();
|
|
if (axis != -1) {
|
|
value8 = nextUInt8();
|
|
if (execute) {
|
|
// find previous mapping
|
|
bool found = false;
|
|
for (uint8_t i = 0; i < m_numMappedAxis; ++i) {
|
|
if (m_mappedAxis[i].m_number == axis) {
|
|
m_mappedAxis[i].m_type = MOUSE_REL_Y_AXIS;
|
|
m_mappedAxis[i].m_value = value8;
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
m_mappedAxis[m_numMappedAxis].m_type = MOUSE_REL_Y_AXIS;
|
|
m_mappedAxis[m_numMappedAxis].m_number = axis;
|
|
m_mappedAxis[m_numMappedAxis++].m_value = value8;
|
|
}
|
|
}
|
|
} else {
|
|
if (execute) {
|
|
// find axis
|
|
for (uint8_t i = 0; i < m_numMappedAxis; ++i) {
|
|
if (m_mappedAxis[i].m_number == axis) {
|
|
m_mappedAxis[i].m_type = MAPPING_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} while (token != MACRO_NULL);
|
|
}
|
|
|
|
void CimditProfile::rotaryAction(uint8_t num, int8_t delta) {
|
|
if (m_displayState != DISPLAY_USER_STRING && m_displayState != DISPLAY_CONFIRMATION) {
|
|
showActiveProfile();
|
|
}
|
|
// find right action
|
|
// rotary has counter-clockwise and clockwise mapping
|
|
num = (num << 1) + (delta > 0);
|
|
for (uint8_t i = 0; i < m_numMappedRotary; ++i) {
|
|
if (m_mappedRotary[i].m_number == num) {
|
|
// rotary supports mouse rel axis, next/prev profile and macro
|
|
switch (m_mappedRotary[i].m_type) {
|
|
case MOUSE_REL_X_AXIS:
|
|
Mouse.move(m_mappedRotary[i].m_value * delta, 0, 0);
|
|
break;
|
|
case MOUSE_REL_Y_AXIS:
|
|
Mouse.move(0, m_mappedRotary[i].m_value * delta, 0);
|
|
break;
|
|
case MOUSE_REL_WHEEL:
|
|
Mouse.move(0, 0, m_mappedRotary[i].m_value * delta);
|
|
break;
|
|
case NEXT_PROFILE:
|
|
case PREV_PROFILE:
|
|
if (m_mappedRotary[i].m_type == NEXT_PROFILE) {
|
|
m_selectingProfile = (m_selectingProfile + 1) % m_numProfiles;
|
|
} else {
|
|
if (m_selectingProfile > 0) {
|
|
--m_selectingProfile;
|
|
} else {
|
|
m_selectingProfile = m_numProfiles - 1;
|
|
}
|
|
}
|
|
showActivate();
|
|
break;
|
|
case MACRO_PRESS:
|
|
scanOrExecuteMacro(m_mappedRotary[i].m_value, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CimditProfile::showActiveProfile() {
|
|
if (m_displayState != DISPLAY_PROFILE) {
|
|
#ifdef HAVE_DISPLAY
|
|
if (m_customImage) {
|
|
m_eepromPosition = m_customImage;
|
|
for (uint8_t y = 0; y < SCREEN_HEIGHT; ++y) {
|
|
for (uint8_t x = 0; x < SCREEN_WIDTH; x += 8) {
|
|
uint8_t img = nextUInt8();
|
|
for (uint8_t t = 0; t < 8; ++t) {
|
|
display.drawPixel(x + t, y, (img >> (7-t)) & 1);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
printTextCentered(m_profiles[m_activeProfile]);
|
|
}
|
|
#else
|
|
Serial.println(m_profiles[m_activeProfile]);
|
|
#endif
|
|
m_displayState = DISPLAY_PROFILE;
|
|
display.display();
|
|
}
|
|
m_stateTimeout = hal.m_millis + PROFILE_TIME;
|
|
m_stateStarted = hal.m_millis;
|
|
}
|
|
|
|
void CimditProfile::showActivate() {
|
|
#ifdef HAVE_DISPLAY
|
|
display.clearDisplay();
|
|
display.setTextSize(2,2);
|
|
display.setCursor(16, 0);
|
|
display.write("activate");
|
|
// default font is 5x8 pixel
|
|
uint8_t w = (strlen(m_profiles[m_selectingProfile]) + 1) * 2 * 6;
|
|
uint8_t h = 16;
|
|
if (w > SCREEN_WIDTH) {
|
|
display.setTextSize(1,1);
|
|
w = w >> 1;
|
|
h = 8;
|
|
}
|
|
display.setCursor((SCREEN_WIDTH - w) >> 1, SCREEN_HEIGHT- h);
|
|
display.write(m_profiles[m_selectingProfile]);
|
|
display.write("?");
|
|
display.display();
|
|
#else
|
|
Serial.print(F("activate "));
|
|
Serial.print(m_profiles[m_selectingProfile]);
|
|
Serial.println(F("?"));
|
|
#endif
|
|
m_displayState = DISPLAY_CONFIRMATION;
|
|
m_stateTimeout = hal.m_millis + SELECTION_TIME;
|
|
m_stateStarted = hal.m_millis;
|
|
}
|
|
|
|
void CimditProfile::printTextCentered(const char* text) {
|
|
display.clearDisplay();
|
|
display.setTextSize(2,2);
|
|
// default font is 6x8 pixel
|
|
uint8_t w = strlen(text) * 12;
|
|
uint8_t h = 16;
|
|
if (w > SCREEN_WIDTH) {
|
|
display.setTextSize(1,1);
|
|
w = w >> 1;
|
|
h = 8;
|
|
}
|
|
display.setCursor((SCREEN_WIDTH - w) >> 1, (SCREEN_HEIGHT - h) >> 1);
|
|
display.write(text);
|
|
display.display();
|
|
}
|
|
|
|
void CimditProfile::showUserString() {
|
|
#ifdef HAVE_DISPLAY
|
|
printTextCentered(m_userString);
|
|
#endif
|
|
m_displayState = DISPLAY_USER_STRING;
|
|
m_stateTimeout = hal.m_millis + m_userTimeout;
|
|
m_stateStarted = hal.m_millis;
|
|
}
|
|
|
|
void CimditProfile::axisAction(uint8_t num, uint16_t state) {
|
|
for (uint8_t i = 0; i < m_numMappedAxis; ++i) {
|
|
if (m_mappedAxis[i].m_number == num) {
|
|
switch (m_mappedAxis[i].m_type) {
|
|
case JOYSTICK_AXIS:
|
|
switch (m_mappedAxis[i].m_value) {
|
|
case 1:
|
|
Gamepad.xAxis(map(state, 0, 1023, -32768, 32767));
|
|
break;
|
|
case 2:
|
|
Gamepad.yAxis(map(state, 0, 1023, -32768, 32767));
|
|
break;
|
|
case 3:
|
|
Gamepad.zAxis(map(state, 0, 1023, -128, 127));
|
|
break;
|
|
case 4:
|
|
Gamepad.rxAxis(map(state, 0, 1023, -32768, 32767));
|
|
break;
|
|
case 5:
|
|
Gamepad.ryAxis(map(state, 0, 1023, -32768, 32767));
|
|
break;
|
|
case 6:
|
|
Gamepad.rzAxis(map(state, 0, 1023, -128, 127));
|
|
break;
|
|
}
|
|
break;
|
|
case MOUSE_REL_X_AXIS: {
|
|
int16_t pos = (int16_t)state - 512;
|
|
if (pos > JOYSTICK_DEAD_ZONE_X) {
|
|
m_mouseMoveX = map(pos, JOYSTICK_DEAD_ZONE_X, 511, 0, m_mappedAxis[i].m_value);
|
|
} else if (pos < -JOYSTICK_DEAD_ZONE_X) {
|
|
m_mouseMoveX = map(pos, -512, -JOYSTICK_DEAD_ZONE_X, -m_mappedAxis[i].m_value, 0);
|
|
} else {
|
|
m_mouseMoveX = 0;
|
|
}
|
|
}
|
|
break;
|
|
case MOUSE_REL_Y_AXIS: {
|
|
int16_t pos = (int16_t)state - 512;
|
|
if (pos > JOYSTICK_DEAD_ZONE_Y) {
|
|
m_mouseMoveY = map(pos, JOYSTICK_DEAD_ZONE_Y, 511, 0, m_mappedAxis[i].m_value);
|
|
} else if (pos < -JOYSTICK_DEAD_ZONE_Y) {
|
|
m_mouseMoveY = map(pos, -512, -JOYSTICK_DEAD_ZONE_Y, -m_mappedAxis[i].m_value, 0);
|
|
} else {
|
|
m_mouseMoveY = 0;
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CimditProfile::buttonAction(uint8_t num, bool state) {
|
|
if (m_displayState != DISPLAY_USER_STRING && m_displayState != DISPLAY_CONFIRMATION) {
|
|
showActiveProfile();
|
|
}
|
|
for (uint8_t i = 0; i < m_numMappedButtons; ++i) {
|
|
if (m_mappedButtons[i].m_number == num) {
|
|
switch (m_mappedButtons[i].m_type) {
|
|
case JOYSTICK_BUTTON:
|
|
if (state) {
|
|
Gamepad.press(m_mappedButtons[i].m_value);
|
|
} else {
|
|
Gamepad.release(m_mappedButtons[i].m_value);
|
|
}
|
|
Gamepad.write();
|
|
break;
|
|
case MOUSE_BUTTON:
|
|
if (state) {
|
|
Mouse.press(m_mappedButtons[i].m_value);
|
|
} else {
|
|
Mouse.release(m_mappedButtons[i].m_value);
|
|
}
|
|
break;
|
|
case MOUSE_REL_X_AXIS:
|
|
Mouse.move(m_mappedButtons[i].m_value, 0, 0);
|
|
break;
|
|
case MOUSE_REL_Y_AXIS:
|
|
Mouse.move(0, m_mappedButtons[i].m_value, 0);
|
|
break;
|
|
case MOUSE_REL_WHEEL:
|
|
Mouse.move(0, 0, m_mappedButtons[i].m_value);
|
|
break;
|
|
case NEXT_PROFILE:
|
|
case PREV_PROFILE:
|
|
if (m_mappedRotary[i].m_type == NEXT_PROFILE) {
|
|
m_selectingProfile = (m_selectingProfile + 1) % m_numProfiles;
|
|
} else {
|
|
if (m_selectingProfile > 0) {
|
|
--m_selectingProfile;
|
|
} else {
|
|
m_selectingProfile = m_numProfiles - 1;
|
|
}
|
|
}
|
|
showActivate();
|
|
break;
|
|
case SWITCH_PROFILE:
|
|
if (m_stateTimeout && state && m_displayState == DISPLAY_CONFIRMATION) {
|
|
load(m_selectingProfile);
|
|
}
|
|
break;
|
|
case MACRO_PRESS:
|
|
if (state)
|
|
scanOrExecuteMacro(m_mappedButtons[i].m_value, true);
|
|
break;
|
|
case MACRO_RELEASE:
|
|
if (!state)
|
|
scanOrExecuteMacro(m_mappedButtons[i].m_value, true);
|
|
break;
|
|
case KEYBOARD_BUTTON:
|
|
if (state) {
|
|
Keyboard.press((KeyboardKeycode)m_mappedButtons[i].m_value);
|
|
} else {
|
|
Keyboard.release((KeyboardKeycode)m_mappedButtons[i].m_value);
|
|
}
|
|
break;
|
|
case MAPPING_NONE:
|
|
case JOYSTICK_AXIS:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CimditProfile::tick() {
|
|
if (m_stateTimeout && hal.m_millis >= m_stateTimeout) {
|
|
switch (m_displayState) {
|
|
case DISPLAY_BLANK: // ignored
|
|
break;
|
|
case DISPLAY_PROFILE: // go to blank display state
|
|
display.clearDisplay();
|
|
display.display();
|
|
m_displayState = DISPLAY_BLANK;
|
|
m_stateTimeout = 0;
|
|
m_stateStarted = hal.m_millis;
|
|
break;
|
|
case DISPLAY_CONFIRMATION:
|
|
case DISPLAY_USER_STRING:
|
|
// go back to profile name
|
|
showActiveProfile();
|
|
m_selectingProfile = m_activeProfile;
|
|
break;
|
|
}
|
|
}
|
|
if (hal.m_millis > m_nextBarUpdate) {
|
|
m_nextBarUpdate = hal.m_millis + BAR_UPDATE_TIME;
|
|
if (m_stateTimeout) {
|
|
uint8_t percentage = SCREEN_WIDTH * (hal.m_millis - m_stateStarted) / (m_stateTimeout - m_stateStarted);
|
|
// white bar from left to percentage
|
|
display.drawFastHLine(0, SCREEN_HEIGHT-1, percentage, true);
|
|
// black bar from percentage to right
|
|
display.drawFastHLine(percentage + 1, SCREEN_HEIGHT-1, SCREEN_WIDTH - percentage - 1, false);
|
|
display.displayPartial(3);
|
|
}
|
|
if (m_displayState == DISPLAY_BLANK) {
|
|
// clear old pixel
|
|
display.drawPixel(m_dotX, m_dotY, false);
|
|
uint8_t part1 = m_dotY/8;
|
|
uint16_t r = rand();
|
|
m_dotX = r & 127;
|
|
m_dotY = r & 31;
|
|
display.drawPixel(m_dotX, m_dotY, true);
|
|
uint8_t part2 = m_dotY/8;
|
|
if (part1 != part2) {
|
|
display.displayPartial(part2);
|
|
}
|
|
display.displayPartial(part1);
|
|
}
|
|
|
|
}
|
|
if (m_mouseMoveX || m_mouseMoveY) {
|
|
m_relMouseX += (float)m_mouseMoveX / 50;
|
|
m_relMouseY += (float)m_mouseMoveY / 50;
|
|
if (fabs(m_relMouseX) >= 1 || fabs(m_relMouseY) >= 1) {
|
|
Mouse.move(m_relMouseX, m_relMouseY);
|
|
m_relMouseX -= (int8_t)m_relMouseX;
|
|
m_relMouseY -= (int8_t)m_relMouseY;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CimditProfile::printFlash() {
|
|
const uint8_t eepromPosition = m_eepromPosition;
|
|
m_eepromPosition = 0;
|
|
uint16_t len = nextUInt16();
|
|
m_eepromPosition = 0;
|
|
#ifdef EXTERNAL_EEPROM
|
|
if (len < 32768) {
|
|
#else
|
|
if (len < 1024) {
|
|
#endif
|
|
uint8_t in;
|
|
for (uint16_t i = 0; i < len; ++i) {
|
|
in = nextUInt8();
|
|
Serial.print(in >> 8, 16);
|
|
Serial.print(in & 255, 16);
|
|
}
|
|
Serial.println();
|
|
}
|
|
m_eepromPosition = eepromPosition;
|
|
}
|
|
|
|
#ifdef EXTERNAL_EEPROM
|
|
void CimditProfile::eepromWrite(uint16_t addr, uint8_t val) {
|
|
Wire.beginTransmission(EXTERNAL_EEPROM);
|
|
Wire.write((uint8_t)(addr >> 8)); // MSB
|
|
Wire.write((uint8_t)(addr & 0xFF)); // LSB
|
|
Wire.write(val);
|
|
Wire.endTransmission();
|
|
delay(5);
|
|
}
|
|
#endif
|
|
|
|
void CimditProfile::writeFlash() {
|
|
char buf[5]= {0};
|
|
Serial.readBytes(buf, 4);
|
|
uint16_t len = strtol(buf, NULL, 16);
|
|
buf[2] = 0;
|
|
m_eepromPosition = 0;
|
|
#ifdef EXTERNAL_EEPROM
|
|
eepromWrite(m_eepromPosition++, len >> 8);
|
|
eepromWrite(m_eepromPosition++, len & 255);
|
|
if (len < 32768) {
|
|
#else
|
|
EEPROM.write(m_eepromPosition++, len >> 8);
|
|
EEPROM.write(m_eepromPosition++, len & 255);
|
|
if (len < 1024) {
|
|
#endif
|
|
for (uint16_t i = 2; i < len; ++i) {
|
|
while ((buf[0] = Serial.read()) == -1) {
|
|
delay(100);
|
|
}
|
|
while ((buf[1] = Serial.read()) == -1) {
|
|
delay(100);
|
|
}
|
|
uint8_t in = strtol(buf, NULL, 16);
|
|
#ifdef EXTERNAL_EEPROM
|
|
eepromWrite(m_eepromPosition++, in);
|
|
#else
|
|
EEPROM.write(m_eepromPosition++, in);
|
|
#endif
|
|
}
|
|
Serial.println("written");
|
|
initProfiles();
|
|
}
|
|
m_displayState = DISPLAY_BLANK; // to enforce a redraw
|
|
showActiveProfile();
|
|
return false;
|
|
}
|
|
|
|
void CimditProfile::setUserString(uint8_t timeout, const char* input) {
|
|
strncpy(m_userString, input, sizeof(m_userString) - 1);
|
|
m_userTimeout = timeout * 1000;
|
|
showUserString();
|
|
}
|
|
|
|
void CimditProfile::userDisplay() {
|
|
uint8_t timeout = 0;
|
|
char buf = 0;
|
|
// read number for time to display
|
|
do {
|
|
buf = Serial.read();
|
|
if (buf != '\n') {
|
|
timeout = timeout * 10 + buf - '0';
|
|
}
|
|
} while (buf != '\n');
|
|
Serial.println(F("ready for .pbm file"));
|
|
// read header
|
|
do {
|
|
buf = Serial.read();
|
|
} while (buf != '\n');
|
|
while (!Serial.available()) { delay(10); };
|
|
buf = Serial.read();
|
|
if (buf == '#') { // a comment
|
|
do {
|
|
buf = Serial.read();
|
|
} while (buf != '\n');
|
|
}
|
|
while (!Serial.available()) { delay(10); };
|
|
// width height
|
|
uint8_t width = 0;
|
|
do {
|
|
buf = Serial.read();
|
|
|
|
if (buf != ' ') {
|
|
width = width * 10 + buf - '0';
|
|
}
|
|
} while (buf != ' ');
|
|
uint8_t height = 0;
|
|
do {
|
|
buf = Serial.read();
|
|
if (buf != '\n') {
|
|
height = height * 10 + buf - '0';
|
|
}
|
|
} while (buf != '\n');
|
|
buf = 0;
|
|
uint16_t len = width * height;
|
|
if (width != SCREEN_WIDTH || SCREEN_HEIGHT != 32) {
|
|
for (uint8_t i = 0; i < width * height; ++i) {
|
|
buf = Serial.read();
|
|
if (buf == '\n') buf = Serial.read();
|
|
}
|
|
return;
|
|
}
|
|
for (uint8_t y = 0; y < SCREEN_HEIGHT; ++y) {
|
|
for (uint8_t x = 0; x < SCREEN_WIDTH; ++x) {
|
|
while ((buf = Serial.read()) == -1) {
|
|
delay(100);
|
|
}
|
|
while (buf != '0' && buf != '1') {
|
|
buf = Serial.read();
|
|
}
|
|
display.drawPixel(x, y, buf == '1');
|
|
}
|
|
}
|
|
display.display();
|
|
m_displayState = DISPLAY_USER_STRING;
|
|
uint32_t now = millis();
|
|
m_stateTimeout = now + (uint32_t)timeout * 1000;
|
|
m_stateStarted = now;
|
|
}
|